{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "all-stories",
  "title": "All Stories",
  "author": "Lloyd Richards <lloyd.d.richards@gmail.com>",
  "description": "Complete collection of all Storybook stories in the registry for bulk installation",
  "registryDependencies": [
    "accordion",
    "alert",
    "alert-dialog",
    "aspect-ratio",
    "attachment",
    "avatar",
    "badge",
    "breadcrumb",
    "bubble",
    "button",
    "calendar",
    "card",
    "carousel",
    "chart",
    "checkbox",
    "collapsible",
    "dialog",
    "command",
    "context-menu",
    "drawer",
    "dropdown-menu",
    "label",
    "hover-card",
    "input",
    "input-group",
    "input-otp",
    "marker",
    "menubar",
    "message",
    "message-scroller",
    "navigation-menu",
    "popover",
    "progress",
    "questionnaire",
    "radio-group",
    "resizable",
    "scroll-area",
    "select",
    "separator",
    "sheet",
    "sidebar",
    "skeleton",
    "slider",
    "switch",
    "table",
    "tabs",
    "textarea",
    "toggle",
    "toggle-group",
    "tooltip"
  ],
  "files": [
    {
      "path": "registry/ui/accordion-story/accordion-base.stories.tsx",
      "content": "// Replace nextjs-vite with the name of your framework\nimport type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\nimport { expect, userEvent, waitFor, within } from \"storybook/test\";\n\nimport {\n  Accordion,\n  AccordionContent,\n  AccordionItem,\n  AccordionTrigger,\n} from \"@/components/ui/accordion\";\n\n/**\n * A vertically stacked set of interactive headings that each reveal a section\n * of content.\n */\nconst meta = {\n  title: \"ui/base/Accordion\",\n  component: Accordion,\n  tags: [\"autodocs\"],\n  argTypes: {\n    multiple: {\n      control: \"boolean\",\n      description: \"Allow multiple accordion items to be open\",\n    },\n    disabled: {\n      control: \"boolean\",\n    },\n  },\n  args: {\n    multiple: false,\n    disabled: false,\n  },\n  render: (args) => (\n    <Accordion {...args}>\n      <AccordionItem value=\"item-1\">\n        <AccordionTrigger>Is it accessible?</AccordionTrigger>\n        <AccordionContent>\n          Yes. It adheres to the WAI-ARIA design pattern.\n        </AccordionContent>\n      </AccordionItem>\n      <AccordionItem value=\"item-2\">\n        <AccordionTrigger>Is it styled?</AccordionTrigger>\n        <AccordionContent>\n          Yes. It comes with default styles that matches the other components'\n          aesthetic.\n        </AccordionContent>\n      </AccordionItem>\n      <AccordionItem value=\"item-3\">\n        <AccordionTrigger>Is it animated?</AccordionTrigger>\n        <AccordionContent>\n          Yes. It's animated by default, but you can disable it if you prefer.\n        </AccordionContent>\n      </AccordionItem>\n    </Accordion>\n  ),\n} satisfies Meta<typeof Accordion>;\n\nexport default meta;\n\ntype Story = StoryObj<typeof meta>;\n\n/**\n * The default behavior of the accordion allows only one item to be open.\n */\nexport const Default: Story = {};\n\nexport const ShouldOnlyOpenOneWhenSingleType: Story = {\n  name: \"when accordions are clicked, should open only one item at a time\",\n  args: {\n    multiple: false,\n  },\n  tags: [\"!dev\", \"!autodocs\"],\n  play: async ({ canvasElement }) => {\n    const canvas = within(canvasElement);\n    const accordions = await canvas.getAllByRole(\"button\");\n    const expandedCount = () =>\n      accordions.filter(\n        (trigger) => trigger.getAttribute(\"aria-expanded\") === \"true\",\n      ).length;\n\n    // Open the tabs one at a time\n    for (const trigger of accordions) {\n      await userEvent.click(trigger);\n      await waitFor(() => {\n        expect(expandedCount()).toBe(1);\n      });\n    }\n\n    // Close the last opened tab\n    await userEvent.click(accordions[accordions.length - 1]);\n    await waitFor(() => {\n      expect(expandedCount()).toBe(0);\n    });\n  },\n};\n\nexport const ShouldOpenAllWhenMultipleType: Story = {\n  name: \"when accordions are clicked, should open all items one at a time\",\n  args: {\n    multiple: true,\n  },\n  tags: [\"!dev\", \"!autodocs\"],\n  play: async ({ canvasElement }) => {\n    const canvas = within(canvasElement);\n    const accordions = await canvas.getAllByRole(\"button\");\n    const expandedCount = () =>\n      accordions.filter(\n        (trigger) => trigger.getAttribute(\"aria-expanded\") === \"true\",\n      ).length;\n\n    // Open all tabs one at a time\n    for (let i = 0; i < accordions.length; i++) {\n      await userEvent.click(accordions[i]);\n      await waitFor(() => {\n        expect(expandedCount()).toBe(i + 1);\n      });\n    }\n\n    // Close all tabs one at a time\n    for (let i = accordions.length - 1; i > 0; i--) {\n      await userEvent.click(accordions[i]);\n      await waitFor(() => {\n        expect(expandedCount()).toBe(i);\n      });\n    }\n\n    // Close the last opened tab\n    await userEvent.click(accordions[0]);\n    await waitFor(() => {\n      expect(expandedCount()).toBe(0);\n    });\n  },\n};\n",
      "type": "registry:component",
      "target": "@ui/accordion.stories.tsx"
    },
    {
      "path": "registry/ui/alert-story/alert-base.stories.tsx",
      "content": "// Replace nextjs-vite with the name of your framework\nimport type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\nimport { AlertCircle } from \"lucide-react\";\n\nimport {\n  Alert,\n  AlertDescription,\n  AlertTitle,\n} from \"@/components/ui/alert\";\n\n/**\n * Displays a callout for user attention.\n */\nconst meta = {\n  title: \"ui/base/Alert\",\n  component: Alert,\n  tags: [\"autodocs\"],\n  argTypes: {\n    variant: {\n      options: [\"default\", \"destructive\"],\n      control: { type: \"radio\" },\n    },\n  },\n  args: {\n    variant: \"default\",\n  },\n  render: (args) => (\n    <Alert {...args}>\n      <AlertTitle>Heads up!</AlertTitle>\n      <AlertDescription>\n        You can add components to your app using the cli.\n      </AlertDescription>\n    </Alert>\n  ),\n} satisfies Meta<typeof Alert>;\n\nexport default meta;\n\ntype Story = StoryObj<typeof meta>;\n/**\n * The default form of the alert.\n */\nexport const Default: Story = {};\n\n/**\n * Use the `destructive` alert to indicate a destructive action.\n */\nexport const Destructive: Story = {\n  render: (args) => (\n    <Alert {...args}>\n      <AlertCircle className=\"h-4 w-4\" />\n      <AlertTitle>Error</AlertTitle>\n      <AlertDescription>\n        Your session has expired. Please log in again.\n      </AlertDescription>\n    </Alert>\n  ),\n  args: {\n    variant: \"destructive\",\n  },\n};\n",
      "type": "registry:component",
      "target": "@ui/alert.stories.tsx"
    },
    {
      "path": "registry/ui/alert-dialog-story/alert-dialog-base.stories.tsx",
      "content": "// Replace nextjs-vite with the name of your framework\nimport type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\n\nimport {\n  AlertDialog,\n  AlertDialogAction,\n  AlertDialogCancel,\n  AlertDialogContent,\n  AlertDialogDescription,\n  AlertDialogFooter,\n  AlertDialogHeader,\n  AlertDialogTitle,\n  AlertDialogTrigger,\n} from \"@/components/ui/alert-dialog\";\nimport { userEvent, within } from \"storybook/test\";\n\n/**\n * A modal dialog that interrupts the user with important content and expects\n * a response.\n */\nconst meta = {\n  title: \"ui/base/AlertDialog\",\n  component: AlertDialog,\n  tags: [\"autodocs\"],\n  argTypes: {},\n  render: (args) => (\n    <AlertDialog {...args}>\n      <AlertDialogTrigger>Open</AlertDialogTrigger>\n      <AlertDialogContent>\n        <AlertDialogHeader>\n          <AlertDialogTitle>Are you sure absolutely sure?</AlertDialogTitle>\n          <AlertDialogDescription>\n            This action cannot be undone. This will permanently delete your\n            account and remove your data from our servers.\n          </AlertDialogDescription>\n        </AlertDialogHeader>\n        <AlertDialogFooter>\n          <AlertDialogCancel>Cancel</AlertDialogCancel>\n          <AlertDialogAction>Continue</AlertDialogAction>\n        </AlertDialogFooter>\n      </AlertDialogContent>\n    </AlertDialog>\n  ),\n  parameters: {\n    layout: \"centered\",\n  },\n} satisfies Meta<typeof AlertDialog>;\n\nexport default meta;\n\ntype Story = StoryObj<typeof meta>;\n\n/**\n * The default form of the alert dialog.\n */\nexport const Default: Story = {};\n\nexport const ShouldOpenClose: Story = {\n  name: \"when alert dialog trigger is pressed, should open the dialog and be able to close it\",\n  tags: [\"!dev\", \"!autodocs\"],\n  play: async ({ canvasElement, canvas, step }) => {\n    const canvasBody = within(canvasElement.ownerDocument.body);\n\n    await step(\"open the alert dialog\", async () => {\n      await userEvent.click(\n        await canvas.getByRole(\"button\", {\n          name: /open/i,\n        }),\n      );\n    });\n\n    await step(\"close the alert dialog\", async () => {\n      await userEvent.click(\n        await canvasBody.getByRole(\"button\", {\n          name: /cancel/i,\n        }),\n        { delay: 100 },\n      );\n    });\n  },\n};\n",
      "type": "registry:component",
      "target": "@ui/alert-dialog.stories.tsx"
    },
    {
      "path": "registry/ui/aspect-ratio-story/aspect-ratio-base.stories.tsx",
      "content": "import Image from \"next/image\";\n// Replace nextjs-vite with the name of your framework\nimport type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\n\nimport { AspectRatio } from \"@/components/ui/aspect-ratio\";\n\n/**\n * Displays content within a desired ratio.\n */\nconst meta: Meta<typeof AspectRatio> = {\n  title: \"ui/base/AspectRatio\",\n  component: AspectRatio,\n  tags: [\"autodocs\"],\n  argTypes: {},\n  render: (args) => (\n    <AspectRatio {...args} className=\"bg-slate-50 dark:bg-slate-800\">\n      <Image\n        src=\"https://images.unsplash.com/photo-1576075796033-848c2a5f3696?w=800&dpr=2&q=80\"\n        alt=\"Photo by Alvaro Pinot\"\n        fill\n        unoptimized\n        className=\"rounded-md object-cover\"\n      />\n    </AspectRatio>\n  ),\n  decorators: [\n    (Story) => (\n      <div className=\"w-1/2\">\n        <Story />\n      </div>\n    ),\n  ],\n} satisfies Meta<typeof AspectRatio>;\n\nexport default meta;\n\ntype Story = StoryObj<typeof meta>;\n\n/**\n * The default form of the aspect ratio.\n */\nexport const Default: Story = {\n  args: {\n    ratio: 16 / 9,\n  },\n};\n\n/**\n * Use the `1:1` aspect ratio to display a square image.\n */\nexport const Square: Story = {\n  args: {\n    ratio: 1,\n  },\n};\n\n/**\n * Use the `4:3` aspect ratio to display a landscape image.\n */\nexport const Landscape: Story = {\n  args: {\n    ratio: 4 / 3,\n  },\n};\n\n/**\n * Use the `2.35:1` aspect ratio to display a cinemascope image.\n */\nexport const Cinemascope: Story = {\n  args: {\n    ratio: 2.35 / 1,\n  },\n};\n",
      "type": "registry:component",
      "target": "@ui/aspect-ratio.stories.tsx"
    },
    {
      "path": "registry/ui/attachment-story/attachment-base.stories.tsx",
      "content": "// Replace nextjs-vite with the name of your framework\nimport type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\nimport {\n  CheckIcon,\n  ClockIcon,\n  CopyIcon,\n  DownloadIcon,\n  FileCodeIcon,\n  FileSearchIcon,\n  FileTextIcon,\n  ImageIcon,\n  XIcon,\n} from \"lucide-react\";\nimport Image from \"next/image\";\nimport type * as React from \"react\";\nimport { expect, userEvent, within } from \"storybook/test\";\n\nimport {\n  Attachment,\n  AttachmentAction,\n  AttachmentActions,\n  AttachmentContent,\n  AttachmentDescription,\n  AttachmentGroup,\n  AttachmentMedia,\n  AttachmentTitle,\n  AttachmentTrigger,\n} from \"@/components/ui/attachment\";\nimport {\n  Dialog,\n  DialogContent,\n  DialogDescription,\n  DialogHeader,\n  DialogTitle,\n  DialogTrigger,\n} from \"@/components/ui/dialog\";\nimport { Spinner } from \"@/components/ui/spinner\";\n\n/**\n * Displays file and image attachments with metadata, states, and actions.\n */\nconst meta: Meta<typeof Attachment> = {\n  title: \"ui/base/Attachment\",\n  component: Attachment,\n  tags: [\"autodocs\"],\n  parameters: {\n    layout: \"centered\",\n  },\n  argTypes: {\n    state: {\n      control: \"select\",\n      options: [\"idle\", \"uploading\", \"processing\", \"error\", \"done\"],\n    },\n    size: {\n      control: \"select\",\n      options: [\"default\", \"sm\", \"xs\"],\n    },\n    orientation: {\n      control: \"select\",\n      options: [\"horizontal\", \"vertical\"],\n    },\n  },\n  args: {\n    state: \"done\",\n    size: \"default\",\n    orientation: \"horizontal\",\n  },\n  decorators: (Story) => (\n    <div className=\"w-full min-w-sm max-w-md\">\n      <Story />\n    </div>\n  ),\n} satisfies Meta<typeof Attachment>;\n\nexport default meta;\n\ntype Story = StoryObj<typeof meta>;\n\nfunction FileAttachment({\n  name = \"sales-dashboard.pdf\",\n  description = \"PDF · 2.4 MB\",\n  icon = <FileTextIcon />,\n  actionLabel = \"Remove sales-dashboard.pdf\",\n  ...props\n}: React.ComponentProps<typeof Attachment> & {\n  name?: string;\n  description?: string;\n  icon?: React.ReactNode;\n  actionLabel?: string;\n}) {\n  return (\n    <Attachment {...props}>\n      <AttachmentMedia>{icon}</AttachmentMedia>\n      <AttachmentContent>\n        <AttachmentTitle>{name}</AttachmentTitle>\n        <AttachmentDescription>{description}</AttachmentDescription>\n      </AttachmentContent>\n      <AttachmentActions>\n        <AttachmentAction aria-label={actionLabel}>\n          <XIcon />\n        </AttachmentAction>\n      </AttachmentActions>\n    </Attachment>\n  );\n}\n\n/**\n * The default attachment uses an icon, title, metadata, and an action.\n */\nexport const Default: Story = {\n  render: (args) => <FileAttachment {...args} />,\n};\n\n/**\n * Use image media and vertical orientation for image previews.\n */\nexport const ImagePreview: Story = {\n  args: {\n    orientation: \"vertical\",\n  },\n  render: (args) => (\n    <Attachment {...args}>\n      <AttachmentMedia variant=\"image\">\n        <Image\n          src=\"https://images.unsplash.com/photo-1497366754035-f200968a6e72?auto=format&fit=crop&w=240&q=80\"\n          alt=\"Workspace\"\n          width={96}\n          height={96}\n          unoptimized\n        />\n      </AttachmentMedia>\n      <AttachmentContent>\n        <AttachmentTitle>workspace.png</AttachmentTitle>\n        <AttachmentDescription>PNG · 820 KB</AttachmentDescription>\n      </AttachmentContent>\n      <AttachmentActions>\n        <AttachmentAction aria-label=\"Remove workspace.png\">\n          <XIcon />\n        </AttachmentAction>\n      </AttachmentActions>\n    </Attachment>\n  ),\n};\n\n/**\n * Use the uploading state for active file uploads.\n */\nexport const Uploading: Story = {\n  args: {\n    state: \"uploading\",\n  },\n  render: (args) => (\n    <FileAttachment\n      {...args}\n      name=\"design-system.zip\"\n      description=\"Uploading · 64%\"\n      icon={<Spinner />}\n    />\n  ),\n};\n\n/**\n * Use the processing state after upload while a file is being prepared.\n */\nexport const Processing: Story = {\n  args: {\n    state: \"processing\",\n  },\n  render: (args) => (\n    <FileAttachment\n      {...args}\n      name=\"market-research.pdf\"\n      description=\"Processing document\"\n      icon={<ClockIcon />}\n    />\n  ),\n};\n\n/**\n * Use the error state when an attachment needs user attention.\n */\nexport const ErrorState: Story = {\n  args: {\n    state: \"error\",\n  },\n  render: (args) => (\n    <FileAttachment\n      {...args}\n      name=\"financial-model.xlsx\"\n      description=\"Upload failed. Try again.\"\n      icon={<XIcon />}\n    />\n  ),\n};\n\n/**\n * Use the small size for compact attachment rows.\n */\nexport const Small: Story = {\n  args: {\n    size: \"sm\",\n  },\n  render: (args) => (\n    <FileAttachment\n      {...args}\n      name=\"briefing-notes.pdf\"\n      description=\"PDF · 1.4 MB\"\n    />\n  ),\n};\n\n/**\n * Use the extra small size for dense composer previews.\n */\nexport const ExtraSmall: Story = {\n  args: {\n    size: \"xs\",\n  },\n  render: (args) => (\n    <FileAttachment\n      {...args}\n      name=\"renderer.tsx\"\n      description=\"TSX · 12 KB\"\n      icon={<FileCodeIcon />}\n    />\n  ),\n};\n\n/**\n * Group attachments in a horizontally scrollable row.\n */\nexport const Group: Story = {\n  render: () => (\n    <AttachmentGroup tabIndex={0} role=\"group\" aria-label=\"Attachments\">\n      <FileAttachment name=\"briefing-notes.pdf\" description=\"PDF · 1.4 MB\" />\n      <FileAttachment\n        name=\"workspace.png\"\n        description=\"PNG · 820 KB\"\n        icon={<ImageIcon />}\n      />\n      <FileAttachment\n        name=\"customers.csv\"\n        description=\"CSV · 18 KB\"\n        icon={<FileTextIcon />}\n      />\n    </AttachmentGroup>\n  ),\n};\n\n/**\n * Add multiple icon actions when the attachment supports file operations.\n */\nexport const WithActions: Story = {\n  render: (args) => (\n    <Attachment {...args}>\n      <AttachmentMedia>\n        <FileTextIcon />\n      </AttachmentMedia>\n      <AttachmentContent>\n        <AttachmentTitle>release-notes.pdf</AttachmentTitle>\n        <AttachmentDescription>PDF · 980 KB</AttachmentDescription>\n      </AttachmentContent>\n      <AttachmentActions>\n        <AttachmentAction aria-label=\"Download release-notes.pdf\">\n          <DownloadIcon />\n        </AttachmentAction>\n        <AttachmentAction aria-label=\"Copy link to release-notes.pdf\">\n          <CopyIcon />\n        </AttachmentAction>\n      </AttachmentActions>\n    </Attachment>\n  ),\n};\n\n/**\n * Use AttachmentTrigger for card-level preview while keeping attachment actions separate.\n */\nexport const Trigger: Story = {\n  render: (args) => (\n    <Dialog>\n      <Attachment {...args}>\n        <AttachmentMedia>\n          <FileSearchIcon />\n        </AttachmentMedia>\n        <AttachmentContent>\n          <AttachmentTitle>research-summary.pdf</AttachmentTitle>\n          <AttachmentDescription>Open preview dialog</AttachmentDescription>\n        </AttachmentContent>\n        <AttachmentActions>\n          <AttachmentAction aria-label=\"Remove research-summary.pdf\">\n            <XIcon />\n          </AttachmentAction>\n        </AttachmentActions>\n        <DialogTrigger\n          render={\n            <AttachmentTrigger aria-label=\"Preview research-summary.pdf\" />\n          }\n        />\n      </Attachment>\n      <DialogContent>\n        <DialogHeader>\n          <DialogTitle>research-summary.pdf</DialogTitle>\n          <DialogDescription>\n            Preview the selected attachment before sending it.\n          </DialogDescription>\n        </DialogHeader>\n      </DialogContent>\n    </Dialog>\n  ),\n};\n\n/**\n * Verify attachment actions are independently clickable.\n */\nexport const ActionInteraction: Story = {\n  tags: [\"!dev\", \"!autodocs\"],\n  render: () => <FileAttachment actionLabel=\"Remove sales-dashboard.pdf\" />,\n  play: async ({ canvasElement }) => {\n    const canvas = within(canvasElement);\n    const button = canvas.getByRole(\"button\", {\n      name: /remove sales-dashboard.pdf/i,\n    });\n\n    await userEvent.click(button);\n    expect(button).toHaveFocus();\n  },\n};\n\n/**\n * Verify triggers and actions remain separate interactive controls.\n */\nexport const TriggerInteraction: Story = {\n  tags: [\"!dev\", \"!autodocs\"],\n  render: () => (\n    <Attachment>\n      <AttachmentMedia>\n        <CheckIcon />\n      </AttachmentMedia>\n      <AttachmentContent>\n        <AttachmentTitle>handoff.md</AttachmentTitle>\n        <AttachmentDescription>Markdown · 8 KB</AttachmentDescription>\n      </AttachmentContent>\n      <AttachmentActions>\n        <AttachmentAction aria-label=\"Remove handoff.md\">\n          <XIcon />\n        </AttachmentAction>\n      </AttachmentActions>\n      <AttachmentTrigger aria-label=\"Open handoff.md\" />\n    </Attachment>\n  ),\n  play: async ({ canvasElement }) => {\n    const canvas = within(canvasElement);\n\n    expect(\n      canvas.getByRole(\"button\", { name: /open handoff.md/i }),\n    ).toBeInTheDocument();\n    expect(\n      canvas.getByRole(\"button\", { name: /remove handoff.md/i }),\n    ).toBeInTheDocument();\n  },\n};\n",
      "type": "registry:component",
      "target": "@ui/attachment.stories.tsx"
    },
    {
      "path": "registry/ui/avatar-story/avatar-base.stories.tsx",
      "content": "// Replace nextjs-vite with the name of your framework\nimport type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\n\nimport {\n  Avatar,\n  AvatarFallback,\n  AvatarImage,\n} from \"@/components/ui/avatar\";\n\n/**\n * An image element with a fallback for representing the user.\n */\nconst meta = {\n  title: \"ui/base/Avatar\",\n  component: Avatar,\n  tags: [\"autodocs\"],\n  argTypes: {},\n  render: (args) => (\n    <Avatar {...args}>\n      <AvatarImage src=\"https://github.com/shadcn.png\" />\n      <AvatarFallback>CN</AvatarFallback>\n    </Avatar>\n  ),\n  parameters: {\n    layout: \"centered\",\n  },\n} satisfies Meta<typeof Avatar>;\n\nexport default meta;\n\ntype Story = StoryObj<typeof meta>;\n\n/**\n * The default form of the avatar.\n */\nexport const Default: Story = {};\n",
      "type": "registry:component",
      "target": "@ui/avatar.stories.tsx"
    },
    {
      "path": "registry/ui/badge-story/badge-base.stories.tsx",
      "content": "// Replace nextjs-vite with the name of your framework\nimport type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\n\nimport { Badge } from \"@/components/ui/badge\";\n\n/**\n * Displays a badge or a component that looks like a badge.\n */\nconst meta = {\n  title: \"ui/base/Badge\",\n  component: Badge,\n  tags: [\"autodocs\"],\n  argTypes: {\n    variant: {\n      control: \"select\",\n      options: [\"default\", \"secondary\", \"destructive\", \"outline\"],\n    },\n    children: {\n      control: \"text\",\n      description: \"Badge content\",\n    },\n  },\n  args: {\n    variant: \"default\",\n    children: \"Badge\",\n  },\n  parameters: {\n    layout: \"centered\",\n  },\n} satisfies Meta<typeof Badge>;\n\nexport default meta;\n\ntype Story = StoryObj<typeof meta>;\n\n/**\n * The default form of the badge.\n */\nexport const Default: Story = {};\n\n/**\n * Use the `secondary` badge to call for less urgent information, blending\n * into the interface while still signaling minor updates or statuses.\n */\nexport const Secondary: Story = {\n  args: {\n    variant: \"secondary\",\n  },\n};\n\n/**\n * Use the `destructive` badge to  indicate errors, alerts, or the need for\n * immediate attention.\n */\nexport const Destructive: Story = {\n  args: {\n    variant: \"destructive\",\n  },\n};\n\n/**\n * Use the `outline` badge for overlaying without obscuring interface details,\n * emphasizing clarity and subtlety..\n */\nexport const Outline: Story = {\n  args: {\n    variant: \"outline\",\n  },\n};\n",
      "type": "registry:component",
      "target": "@ui/badge.stories.tsx"
    },
    {
      "path": "registry/ui/breadcrumb-story/breadcrumb-base.stories.tsx",
      "content": "// Replace nextjs-vite with the name of your framework\nimport type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\nimport { ArrowRightSquare } from \"lucide-react\";\n\nimport {\n  Breadcrumb,\n  BreadcrumbItem,\n  BreadcrumbLink,\n  BreadcrumbList,\n  BreadcrumbPage,\n  BreadcrumbSeparator,\n} from \"@/components/ui/breadcrumb\";\n\n/**\n * Displays the path to the current resource using a hierarchy of links.\n */\nconst meta = {\n  title: \"ui/base/Breadcrumb\",\n  component: Breadcrumb,\n  tags: [\"autodocs\"],\n  argTypes: {},\n  args: {},\n  render: (args) => (\n    <Breadcrumb {...args}>\n      <BreadcrumbList>\n        <BreadcrumbItem>\n          <BreadcrumbLink>Home</BreadcrumbLink>\n        </BreadcrumbItem>\n        <BreadcrumbSeparator />\n        <BreadcrumbItem>\n          <BreadcrumbLink>Components</BreadcrumbLink>\n        </BreadcrumbItem>\n        <BreadcrumbSeparator />\n        <BreadcrumbItem>\n          <BreadcrumbPage>Breadcrumb</BreadcrumbPage>\n        </BreadcrumbItem>\n      </BreadcrumbList>\n    </Breadcrumb>\n  ),\n  parameters: {\n    layout: \"centered\",\n  },\n} satisfies Meta<typeof Breadcrumb>;\n\nexport default meta;\n\ntype Story = StoryObj<typeof meta>;\n\n/**\n * Displays the path of links to the current resource.\n */\nexport const Default: Story = {};\n\n/**\n * Displays the path with a custom icon for the separator.\n */\nexport const WithCustomSeparator: Story = {\n  render: (args) => (\n    <Breadcrumb {...args}>\n      <BreadcrumbList>\n        <BreadcrumbItem>\n          <BreadcrumbLink>Home</BreadcrumbLink>\n        </BreadcrumbItem>\n        <BreadcrumbSeparator>\n          <ArrowRightSquare />\n        </BreadcrumbSeparator>\n        <BreadcrumbItem>\n          <BreadcrumbLink>Components</BreadcrumbLink>\n        </BreadcrumbItem>\n        <BreadcrumbSeparator>\n          <ArrowRightSquare />\n        </BreadcrumbSeparator>\n        <BreadcrumbItem>\n          <BreadcrumbPage>Breadcrumb</BreadcrumbPage>\n        </BreadcrumbItem>\n      </BreadcrumbList>\n    </Breadcrumb>\n  ),\n};\n",
      "type": "registry:component",
      "target": "@ui/breadcrumb.stories.tsx"
    },
    {
      "path": "registry/ui/bubble-story/bubble-base.stories.tsx",
      "content": "// Replace nextjs-vite with the name of your framework\nimport type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\nimport { CheckIcon, InfoIcon, ThumbsUpIcon } from \"lucide-react\";\nimport type * as React from \"react\";\nimport { expect, userEvent, within } from \"storybook/test\";\nimport {\n  Bubble,\n  BubbleContent,\n  BubbleGroup,\n  BubbleReactions,\n} from \"@/components/ui/bubble\";\nimport { Button } from \"@/components/ui/button\";\nimport {\n  Collapsible,\n  CollapsibleContent,\n  CollapsibleTrigger,\n} from \"@/components/ui/collapsible\";\nimport {\n  Popover,\n  PopoverContent,\n  PopoverTrigger,\n} from \"@/components/ui/popover\";\nimport {\n  Tooltip,\n  TooltipContent,\n  TooltipProvider,\n  TooltipTrigger,\n} from \"@/components/ui/tooltip\";\n\n/**\n * Displays conversational content in a framed or unframed message bubble.\n */\nconst meta: Meta<typeof Bubble> = {\n  title: \"ui/base/Bubble\",\n  component: Bubble,\n  tags: [\"autodocs\"],\n  parameters: {\n    layout: \"centered\",\n  },\n  argTypes: {\n    variant: {\n      control: \"select\",\n      options: [\n        \"default\",\n        \"secondary\",\n        \"muted\",\n        \"tinted\",\n        \"outline\",\n        \"ghost\",\n        \"destructive\",\n      ],\n    },\n    align: {\n      control: \"select\",\n      options: [\"start\", \"end\"],\n    },\n  },\n  args: {\n    variant: \"default\",\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 Bubble>;\n\nexport default meta;\n\ntype Story = StoryObj<typeof meta>;\n\nfunction BubbleExample({\n  children,\n  ...props\n}: React.ComponentProps<typeof Bubble>) {\n  return (\n    <Bubble {...props}>\n      <BubbleContent>{children}</BubbleContent>\n    </Bubble>\n  );\n}\n\n/**\n * The default bubble is a strong primary surface for current-user messages.\n */\nexport const Default: Story = {\n  render: (args) => (\n    <BubbleExample {...args}>\n      I checked the registry output and removed the stale route.\n    </BubbleExample>\n  ),\n};\n\n/**\n * Use the secondary variant for standard conversation content.\n */\nexport const Secondary: Story = {\n  args: {\n    variant: \"secondary\",\n  },\n  render: (args) => (\n    <BubbleExample {...args}>\n      The component JSON now lives under the UI registry.\n    </BubbleExample>\n  ),\n};\n\n/**\n * Use the muted variant for quiet supporting content.\n */\nexport const Muted: Story = {\n  args: {\n    variant: \"muted\",\n  },\n  render: (args) => (\n    <BubbleExample {...args}>\n      This note has lower emphasis than the main reply.\n    </BubbleExample>\n  ),\n};\n\n/**\n * Use the tinted variant for a softer primary treatment.\n */\nexport const Tinted: Story = {\n  args: {\n    variant: \"tinted\",\n  },\n  render: (args) => (\n    <BubbleExample {...args}>\n      I can preserve the chat tone without using the full primary color.\n    </BubbleExample>\n  ),\n};\n\n/**\n * Use the outline variant for secondary rich content.\n */\nexport const Outline: Story = {\n  args: {\n    variant: \"outline\",\n  },\n  render: (args) => (\n    <BubbleExample {...args}>\n      Here is the command output you asked me to review.\n    </BubbleExample>\n  ),\n};\n\n/**\n * Use the destructive variant for failed actions or error messages.\n */\nexport const Destructive: Story = {\n  args: {\n    variant: \"destructive\",\n  },\n  render: (args) => (\n    <BubbleExample {...args}>\n      Failed to send. Check your connection and try again.\n    </BubbleExample>\n  ),\n};\n\n/**\n * Use the ghost variant for assistant text or markdown-like content.\n */\nexport const Ghost: Story = {\n  args: {\n    variant: \"ghost\",\n  },\n  render: (args) => (\n    <BubbleExample {...args}>\n      Ghost bubbles work well when assistant messages should read like regular\n      text instead of framed chat surfaces.\n    </BubbleExample>\n  ),\n};\n\n/**\n * Align a bubble to the end for current-user messages.\n */\nexport const AlignedEnd: Story = {\n  args: {\n    align: \"end\",\n  },\n  render: (args) => (\n    <BubbleExample {...args}>Sure. Hit me with your best demo.</BubbleExample>\n  ),\n};\n\n/**\n * Group consecutive bubbles from the same sender.\n */\nexport const Group: Story = {\n  render: () => (\n    <BubbleGroup>\n      <Bubble variant=\"secondary\">\n        <BubbleContent>Can you tell me what changed?</BubbleContent>\n      </Bubble>\n      <Bubble variant=\"secondary\">\n        <BubbleContent>The registry entries now match the files.</BubbleContent>\n      </Bubble>\n    </BubbleGroup>\n  ),\n};\n\n/**\n * Render bubble content as a button for quick replies.\n */\nexport const ButtonBubble: Story = {\n  args: {\n    variant: \"muted\",\n  },\n  render: (args) => (\n    <Bubble {...args}>\n      <BubbleContent render={<button type=\"button\" />}>\n        I forgot my password\n      </BubbleContent>\n    </Bubble>\n  ),\n};\n\n/**\n * Use reactions for emoji counts or compact quick actions.\n */\nexport const Reactions: Story = {\n  args: {\n    variant: \"secondary\",\n  },\n  render: (args) => (\n    <Bubble {...args} className=\"mb-4\">\n      <BubbleContent>\n        Tests passed on the first try. Looking good.\n      </BubbleContent>\n      <BubbleReactions role=\"img\" aria-label=\"Reactions: thumbs up and party\">\n        <span>👍</span>\n        <span>🎉</span>\n      </BubbleReactions>\n    </Bubble>\n  ),\n};\n\n/**\n * Compose long content with Collapsible for show more interactions.\n */\nexport const CollapsibleBubble: Story = {\n  args: {\n    variant: \"secondary\",\n  },\n  render: (args) => (\n    <Collapsible className=\"flex flex-col gap-2\">\n      <Bubble {...args}>\n        <BubbleContent>\n          The accessibility review found two focus states that were visually too\n          subtle in dark mode.\n        </BubbleContent>\n      </Bubble>\n      <CollapsibleContent>\n        <Bubble {...args}>\n          <BubbleContent>\n            I checked the dialog, menu, and drawer paths because each one\n            renders focusable controls inside an overlay.\n          </BubbleContent>\n        </Bubble>\n      </CollapsibleContent>\n      <CollapsibleTrigger className=\"w-fit text-sm underline underline-offset-4 hover:text-primary\">\n        Show more\n      </CollapsibleTrigger>\n    </Collapsible>\n  ),\n};\n\n/**\n * Pair a bubble with Tooltip to reveal metadata on hover or focus.\n */\nexport const TooltipBubble: Story = {\n  args: {\n    variant: \"secondary\",\n  },\n  render: (args) => (\n    <TooltipProvider>\n      <Tooltip>\n        <TooltipTrigger render={<div />}>\n          <Bubble {...args}>\n            <BubbleContent>Yes, removed it from the registry.</BubbleContent>\n          </Bubble>\n        </TooltipTrigger>\n        <TooltipContent>Read yesterday</TooltipContent>\n      </Tooltip>\n    </TooltipProvider>\n  ),\n};\n\n/**\n * Pair a bubble with Popover to show additional detail on demand.\n */\nexport const PopoverBubble: Story = {\n  args: {\n    variant: \"destructive\",\n  },\n  render: (args) => (\n    <Popover>\n      <Bubble {...args}>\n        <BubbleContent render={<PopoverTrigger />}>\n          Failed to run the command.\n        </BubbleContent>\n      </Bubble>\n      <PopoverContent className=\"max-w-xs text-sm\">\n        The process exited before Storybook finished loading.\n      </PopoverContent>\n    </Popover>\n  ),\n};\n\n/**\n * Verify quick-reply bubbles expose a clickable button.\n */\nexport const ButtonInteraction: Story = {\n  tags: [\"!dev\", \"!autodocs\"],\n  args: {\n    variant: \"muted\",\n  },\n  render: (args) => (\n    <Bubble {...args}>\n      <BubbleContent render={<button type=\"button\" />}>\n        I need help with my subscription\n      </BubbleContent>\n    </Bubble>\n  ),\n  play: async ({ canvasElement }) => {\n    const canvas = within(canvasElement);\n    const button = canvas.getByRole(\"button\", { name: /subscription/i });\n\n    await userEvent.click(button);\n    expect(button).toHaveFocus();\n  },\n};\n\n/**\n * Verify collapsible bubble content can be expanded.\n */\nexport const CollapsibleExpanded: Story = {\n  tags: [\"!dev\", \"!autodocs\"],\n  args: {\n    variant: \"secondary\",\n  },\n  render: (args) => (\n    <Collapsible className=\"flex flex-col gap-2\">\n      <Bubble {...args}>\n        <BubbleContent>The short summary is visible first.</BubbleContent>\n      </Bubble>\n      <CollapsibleContent>\n        <Bubble {...args}>\n          <BubbleContent>\n            The hidden implementation detail is now visible.\n          </BubbleContent>\n        </Bubble>\n      </CollapsibleContent>\n      <CollapsibleTrigger className=\"w-fit text-sm underline underline-offset-4 hover:text-primary\">\n        Show more\n      </CollapsibleTrigger>\n    </Collapsible>\n  ),\n  play: async ({ canvasElement }) => {\n    const canvas = within(canvasElement);\n\n    await userEvent.click(canvas.getByRole(\"button\", { name: /show more/i }));\n    expect(canvas.getByText(/hidden implementation detail/i)).toBeVisible();\n  },\n};\n\n/**\n * Verify interactive reaction buttons have accessible labels.\n */\nexport const AccessibleReactions: Story = {\n  tags: [\"!dev\", \"!autodocs\"],\n  args: {\n    variant: \"secondary\",\n  },\n  render: (args) => (\n    <Bubble {...args} className=\"mb-4\">\n      <BubbleContent>Can I run the formatter?</BubbleContent>\n      <BubbleReactions>\n        <Button aria-label=\"Approve\" size=\"icon-xs\" variant=\"secondary\">\n          <CheckIcon />\n        </Button>\n        <Button aria-label=\"Thumbs up\" size=\"icon-xs\" variant=\"secondary\">\n          <ThumbsUpIcon />\n        </Button>\n        <Button\n          aria-label=\"More information\"\n          size=\"icon-xs\"\n          variant=\"secondary\"\n        >\n          <InfoIcon />\n        </Button>\n      </BubbleReactions>\n    </Bubble>\n  ),\n  play: async ({ canvasElement }) => {\n    const canvas = within(canvasElement);\n\n    expect(\n      canvas.getByRole(\"button\", { name: /approve/i }),\n    ).toBeInTheDocument();\n    expect(\n      canvas.getByRole(\"button\", { name: /thumbs up/i }),\n    ).toBeInTheDocument();\n  },\n};\n",
      "type": "registry:component",
      "target": "@ui/bubble.stories.tsx"
    },
    {
      "path": "registry/ui/button-story/button-base.stories.tsx",
      "content": "// Replace nextjs-vite with the name of your framework\nimport type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\nimport { Loader2, Mail } from \"lucide-react\";\n\nimport { Button } from \"@/components/ui/button\";\n\n/**\n * Displays a button or a component that looks like a button.\n */\nconst meta: Meta<typeof Button> = {\n  title: \"ui/base/Button\",\n  component: Button,\n  tags: [\"autodocs\"],\n  argTypes: {\n    variant: {\n      control: \"select\",\n      options: [\n        \"default\",\n        \"destructive\",\n        \"outline\",\n        \"secondary\",\n        \"ghost\",\n        \"link\",\n      ],\n    },\n    size: {\n      control: \"select\",\n      options: [\n        \"default\",\n        \"xs\",\n        \"sm\",\n        \"lg\",\n        \"icon\",\n        \"icon-xs\",\n        \"icon-sm\",\n        \"icon-lg\",\n      ],\n      if: { arg: \"variant\", neq: \"link\" },\n    },\n    children: {\n      control: \"text\",\n    },\n    disabled: {\n      control: \"boolean\",\n    },\n  },\n  parameters: {\n    layout: \"centered\",\n  },\n  args: {\n    variant: \"default\",\n    size: \"default\",\n    children: \"Button\",\n    disabled: false,\n  },\n} satisfies Meta<typeof Button>;\n\nexport default meta;\n\ntype Story = StoryObj<typeof meta>;\n\n/**\n * The default form of the button, used for primary actions and commands.\n */\nexport const Default: Story = {};\n\n/**\n * Use the `outline` button to reduce emphasis on secondary actions, such as\n * canceling or dismissing a dialog.\n */\nexport const Outline: Story = {\n  args: {\n    variant: \"outline\",\n  },\n};\n\n/**\n * Use the `ghost` button is minimalistic and subtle, for less intrusive\n * actions.\n */\nexport const Ghost: Story = {\n  args: {\n    variant: \"ghost\",\n  },\n};\n\n/**\n * Use the `secondary` button to call for less emphasized actions, styled to\n * complement the primary button while being less conspicuous.\n */\nexport const Secondary: Story = {\n  args: {\n    variant: \"secondary\",\n  },\n};\n\n/**\n * Use the `destructive` button to indicate errors, alerts, or the need for\n * immediate attention.\n */\nexport const Destructive: Story = {\n  args: {\n    variant: \"destructive\",\n  },\n};\n\n/**\n * Use the `link` button to reduce emphasis on tertiary actions, such as\n * hyperlink or navigation, providing a text-only interactive element.\n */\nexport const Link: Story = {\n  args: {\n    variant: \"link\",\n  },\n};\n\n/**\n * Add the `disabled` prop to a button to prevent interactions and add a\n * loading indicator, such as a spinner, to signify an in-progress action.\n */\nexport const Loading: Story = {\n  render: (args) => (\n    <Button {...args}>\n      <Loader2 className=\"mr-2 h-4 w-4 animate-spin\" />\n      Button\n    </Button>\n  ),\n  args: {\n    ...Outline.args,\n    disabled: true,\n  },\n};\n\n/**\n * Add an icon element to a button to enhance visual communication and\n * providing additional context for the action.\n */\nexport const WithIcon: Story = {\n  render: (args) => (\n    <Button {...args}>\n      <Mail className=\"mr-2 h-4 w-4\" /> Login with Email Button\n    </Button>\n  ),\n  args: {\n    ...Secondary.args,\n  },\n};\n\n/**\n * Use the `sm` size for a smaller button, suitable for interfaces needing\n * compact elements without sacrificing usability.\n */\nexport const Small: Story = {\n  args: {\n    size: \"sm\",\n  },\n};\n\n/**\n * Use the `lg` size for a larger button, offering better visibility and\n * easier interaction for users.\n */\nexport const Large: Story = {\n  args: {\n    size: \"lg\",\n  },\n};\n\n/**\n * Use the \"icon\" size for a button with only an icon.\n */\nexport const Icon: Story = {\n  args: {\n    ...Secondary.args,\n    size: \"icon\",\n    title: \"Mail\",\n    children: <Mail />,\n  },\n};\n\n/**\n * Use the `icon-sm` size for a smaller icon-only button.\n */\nexport const IconSmall: Story = {\n  args: {\n    variant: \"secondary\",\n    size: \"icon-sm\",\n    title: \"Mail\",\n    children: <Mail />,\n  },\n};\n\n/**\n * Use the `icon-lg` size for a larger icon-only button.\n */\nexport const IconLarge: Story = {\n  args: {\n    variant: \"secondary\",\n    size: \"icon-lg\",\n    title: \"Mail\",\n    children: <Mail />,\n  },\n};\n\n/**\n * Add the `disabled` prop to prevent interactions with the button.\n */\nexport const Disabled: Story = {\n  args: {\n    disabled: true,\n  },\n};\n",
      "type": "registry:component",
      "target": "@ui/button.stories.tsx"
    },
    {
      "path": "registry/ui/calendar-story/calendar-base.stories.tsx",
      "content": "import type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\nimport { addDays } from \"date-fns\";\nimport { action } from \"storybook/actions\";\nimport { expect, userEvent } from \"storybook/test\";\n\nimport { Calendar } from \"@/components/ui/calendar\";\n\n/**\n * A date field component that allows users to enter and edit date.\n */\nconst meta = {\n  title: \"ui/base/Calendar\",\n  component: Calendar,\n  tags: [\"autodocs\"],\n  argTypes: {\n    mode: {\n      table: {\n        disable: true,\n      },\n    },\n    disabled: {\n      control: \"boolean\",\n    },\n    numberOfMonths: {\n      control: \"number\",\n      description: \"Number of months to display\",\n    },\n    showOutsideDays: {\n      control: \"boolean\",\n      description: \"Show days that fall outside the current month\",\n    },\n  },\n  args: {\n    mode: \"single\",\n    selected: new Date(),\n    onSelect: action(\"onDayClick\"),\n    className: \"rounded-md border w-fit\",\n    disabled: false,\n    numberOfMonths: 1,\n    showOutsideDays: true,\n  },\n  parameters: {\n    layout: \"centered\",\n  },\n} satisfies Meta<typeof Calendar>;\n\nexport default meta;\n\ntype Story = StoryObj<typeof meta>;\n\n/**\n * The default form of the calendar.\n */\nexport const Default: Story = {};\n\n/**\n * Use the `multiple` mode to select multiple dates.\n */\nexport const Multiple: Story = {\n  args: {\n    min: 1,\n    selected: [new Date(), addDays(new Date(), 2), addDays(new Date(), 8)],\n    mode: \"multiple\",\n  },\n};\n\n/**\n * Use the `range` mode to select a range of dates.\n */\nexport const Range: Story = {\n  args: {\n    selected: {\n      from: new Date(),\n      to: addDays(new Date(), 7),\n    },\n    mode: \"range\",\n  },\n};\n\n/**\n * Use the `disabled` prop to disable specific dates.\n */\nexport const Disabled: Story = {\n  args: {\n    disabled: [\n      addDays(new Date(), 1),\n      addDays(new Date(), 2),\n      addDays(new Date(), 3),\n      addDays(new Date(), 5),\n    ],\n  },\n};\n\n/**\n * Use the `numberOfMonths` prop to display multiple months.\n */\nexport const MultipleMonths: Story = {\n  args: {\n    numberOfMonths: 2,\n    showOutsideDays: false,\n  },\n};\n\nexport const ShouldNavigateMonthsWhenClicked: Story = {\n  name: \"when using the calendar navigation, should change months\",\n  tags: [\"!dev\", \"!autodocs\"],\n  args: {\n    defaultMonth: new Date(2000, 8),\n  },\n  play: async ({ canvas }) => {\n    const title = await canvas.findByText(/2000/i);\n    const startTitle = title.textContent || \"\";\n    const backBtn = await canvas.findByRole(\"button\", {\n      name: /previous/i,\n    });\n    const nextBtn = await canvas.findByRole(\"button\", {\n      name: /next/i,\n    });\n    const steps = 6;\n    for (let i = 0; i < steps / 2; i++) {\n      await userEvent.click(backBtn);\n      expect(title).not.toHaveTextContent(startTitle);\n    }\n    for (let i = 0; i < steps; i++) {\n      await userEvent.click(nextBtn);\n      if (i === steps / 2 - 1) {\n        expect(title).toHaveTextContent(startTitle);\n        continue;\n      }\n      expect(title).not.toHaveTextContent(startTitle);\n    }\n  },\n};\n",
      "type": "registry:component",
      "target": "@ui/calendar.stories.tsx"
    },
    {
      "path": "registry/ui/card-story/card-base.stories.tsx",
      "content": "// Replace nextjs-vite with the name of your framework\nimport type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\nimport { BellRing } from \"lucide-react\";\n\nimport { Button } from \"@/components/ui/button\";\nimport {\n  Card,\n  CardAction,\n  CardContent,\n  CardDescription,\n  CardFooter,\n  CardHeader,\n  CardTitle,\n} from \"@/components/ui/card\";\n\nconst notifications = [\n  {\n    title: \"Your call has been confirmed.\",\n    description: \"1 hour ago\",\n  },\n  {\n    title: \"You have a new message!\",\n    description: \"1 hour ago\",\n  },\n  {\n    title: \"Your subscription is expiring soon!\",\n    description: \"2 hours ago\",\n  },\n];\n\n/**\n * Displays a card with header, content, and footer.\n */\nconst meta = {\n  title: \"ui/base/Card\",\n  component: Card,\n  tags: [\"autodocs\"],\n  argTypes: {},\n  args: {\n    className: \"w-96\",\n  },\n  parameters: {\n    layout: \"centered\",\n  },\n} satisfies Meta<typeof Card>;\n\nexport default meta;\n\ntype Story = StoryObj<typeof meta>;\n\n/**\n * The default form of the card.\n */\nexport const Default: Story = {\n  render: (args) => (\n    <Card {...args}>\n      <CardHeader>\n        <CardTitle>Notifications</CardTitle>\n        <CardDescription>You have 3 unread messages.</CardDescription>\n      </CardHeader>\n      <CardContent className=\"grid gap-4\">\n        {notifications.map((notification, index) => (\n          <div key={index} className=\"flex items-center gap-4\">\n            <BellRing className=\"size-6\" />\n            <div>\n              <p>{notification.title}</p>\n              <p className=\"text-foreground/60\">{notification.description}</p>\n            </div>\n          </div>\n        ))}\n      </CardContent>\n      <CardFooter>\n        <Button variant=\"link\">Close</Button>\n      </CardFooter>\n    </Card>\n  ),\n};\n\n/**\n * Use the `CardAction` component to add interactive elements in the header.\n */\nexport const WithCardAction: Story = {\n  render: (args) => (\n    <Card {...args}>\n      <CardHeader>\n        <CardTitle>Team Settings</CardTitle>\n        <CardDescription>Manage your team preferences</CardDescription>\n        <CardAction>\n          <Button size=\"sm\" variant=\"outline\">\n            Edit\n          </Button>\n        </CardAction>\n      </CardHeader>\n      <CardContent>\n        <p>Configure team members, permissions, and notifications.</p>\n      </CardContent>\n      <CardFooter>\n        <Button variant=\"ghost\">Cancel</Button>\n        <Button className=\"ml-auto\">Save Changes</Button>\n      </CardFooter>\n    </Card>\n  ),\n};\n\n/**\n * A minimal card with only content, no header or footer.\n */\nexport const MinimalCard: Story = {\n  render: (args) => (\n    <Card {...args}>\n      <CardContent>\n        <p className=\"text-sm\">\n          This is a minimal card with only content. Perfect for displaying\n          simple information without the need for a header or footer.\n        </p>\n      </CardContent>\n    </Card>\n  ),\n};\n\n/**\n * A card with only a header section, no content or footer.\n */\nexport const HeaderOnly: Story = {\n  render: (args) => (\n    <Card {...args}>\n      <CardHeader>\n        <CardTitle>Quick Stats</CardTitle>\n        <CardDescription>\n          Your account summary at a glance. Click for details.\n        </CardDescription>\n      </CardHeader>\n    </Card>\n  ),\n};\n",
      "type": "registry:component",
      "target": "@ui/card.stories.tsx"
    },
    {
      "path": "registry/ui/carousel-story/carousel-base.stories.tsx",
      "content": "import { expect, userEvent } from \"storybook/test\";\n// Replace nextjs-vite with the name of your framework\nimport type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\n\nimport {\n  Carousel,\n  CarouselContent,\n  CarouselItem,\n  CarouselNext,\n  CarouselPrevious,\n} from \"@/components/ui/carousel\";\n\n/**\n * A carousel with motion and swipe built using Embla.\n */\nconst meta: Meta<typeof Carousel> = {\n  title: \"ui/base/Carousel\",\n  component: Carousel,\n  tags: [\"autodocs\"],\n  argTypes: {},\n  args: {\n    className: \"w-full max-w-xs\",\n  },\n  render: (args) => (\n    <Carousel {...args}>\n      <CarouselContent>\n        {Array.from({ length: 5 }).map((_, index) => (\n          <CarouselItem key={index}>\n            <div className=\"flex aspect-square items-center justify-center rounded border bg-card p-6\">\n              <span className=\"font-semibold text-4xl\">{index + 1}</span>\n            </div>\n          </CarouselItem>\n        ))}\n      </CarouselContent>\n      <CarouselPrevious />\n      <CarouselNext />\n    </Carousel>\n  ),\n  parameters: {\n    layout: \"centered\",\n  },\n} satisfies Meta<typeof Carousel>;\n\nexport default meta;\n\ntype Story = StoryObj<typeof meta>;\n\n/**\n * The default form of the carousel.\n */\nexport const Default: Story = {};\n\n/**\n * Use the `basis` utility class to change the size of the carousel.\n */\nexport const Size: Story = {\n  render: (args) => (\n    <Carousel {...args} className=\"mx-12 w-full max-w-xs\">\n      <CarouselContent>\n        {Array.from({ length: 5 }).map((_, index) => (\n          <CarouselItem key={index} className=\"basis-1/3\">\n            <div className=\"flex aspect-square items-center justify-center rounded border bg-card p-6\">\n              <span className=\"font-semibold text-4xl\">{index + 1}</span>\n            </div>\n          </CarouselItem>\n        ))}\n      </CarouselContent>\n      <CarouselPrevious />\n      <CarouselNext />\n    </Carousel>\n  ),\n  args: {\n    className: \"mx-12 w-full max-w-xs\",\n  },\n};\n\nexport const ShouldNavigate: Story = {\n  name: \"when clicking next/previous buttons, should navigate through slides\",\n  tags: [\"!dev\", \"!autodocs\"],\n  play: async ({ canvas, step }) => {\n    const slides = await canvas.findAllByRole(\"group\");\n    expect(slides).toHaveLength(5);\n    const nextBtn = await canvas.findByRole(\"button\", { name: /next/i });\n    const prevBtn = await canvas.findByRole(\"button\", {\n      name: /previous/i,\n    });\n\n    await step(\"navigate to the last slide\", async () => {\n      for (let i = 0; i < slides.length - 1; i++) {\n        await userEvent.click(nextBtn);\n      }\n    });\n\n    await step(\"navigate back to the first slide\", async () => {\n      for (let i = slides.length - 1; i > 0; i--) {\n        await userEvent.click(prevBtn);\n      }\n    });\n  },\n};\n//\n",
      "type": "registry:component",
      "target": "@ui/carousel.stories.tsx"
    },
    {
      "path": "registry/ui/chart-story/chart-base.stories.tsx",
      "content": "import { useMemo } from \"react\";\n// Replace nextjs-vite with the name of your framework\nimport type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\nimport {\n  Area,\n  AreaChart,\n  Bar,\n  BarChart,\n  CartesianGrid,\n  Label,\n  Line,\n  LineChart,\n  Pie,\n  PieChart,\n  XAxis,\n} from \"recharts\";\n\nimport {\n  type ChartConfig,\n  ChartContainer,\n  ChartTooltip,\n  ChartTooltipContent,\n} from \"@/components/ui/chart\";\n\nconst multiSeriesData = [\n  { month: \"January\", desktop: 186, mobile: 80 },\n  { month: \"February\", desktop: 305, mobile: 200 },\n  { month: \"March\", desktop: 237, mobile: 120 },\n  { month: \"April\", desktop: 73, mobile: 190 },\n  { month: \"May\", desktop: 209, mobile: 130 },\n  { month: \"June\", desktop: 214, mobile: 140 },\n];\n\nconst multiSeriesConfig = {\n  desktop: {\n    label: \"Desktop\",\n    color: \"var(--chart-1)\",\n  },\n  mobile: {\n    label: \"Mobile\",\n    color: \"var(--chart-2)\",\n  },\n} satisfies ChartConfig;\n\nconst singleSeriesData = [\n  { browser: \"chrome\", visitors: 275, fill: \"var(--color-chrome)\" },\n  { browser: \"safari\", visitors: 200, fill: \"var(--color-safari)\" },\n  { browser: \"other\", visitors: 190, fill: \"var(--color-other)\" },\n];\n\nconst singleSeriesConfig = {\n  visitors: {\n    label: \"Visitors\",\n  },\n  chrome: {\n    label: \"Chrome\",\n    color: \"var(--chart-1)\",\n  },\n  safari: {\n    label: \"Safari\",\n    color: \"var(--chart-2)\",\n  },\n  other: {\n    label: \"Other\",\n    color: \"var(--chart-5)\",\n  },\n} satisfies ChartConfig;\n\n/**\n * Beautiful charts. Built using Recharts. Copy and paste into your apps.\n */\nconst meta = {\n  title: \"ui/base/Chart\",\n  component: ChartContainer,\n  tags: [\"autodocs\"],\n  argTypes: {},\n  args: {\n    children: <div />,\n  },\n} satisfies Meta<typeof ChartContainer>;\n\nexport default meta;\n\ntype Story = StoryObj<typeof meta>;\n\n/**\n * Combine multiple Area components to create a stacked area chart.\n */\nexport const StackedAreaChart: Story = {\n  args: {\n    config: multiSeriesConfig,\n  },\n  render: (args) => (\n    <ChartContainer {...args}>\n      <AreaChart\n        accessibilityLayer\n        data={multiSeriesData}\n        margin={{\n          left: 12,\n          right: 12,\n        }}\n      >\n        <CartesianGrid vertical={false} />\n        <XAxis\n          dataKey=\"month\"\n          tickLine={false}\n          axisLine={false}\n          tickMargin={8}\n          tickFormatter={(value) => value.slice(0, 3)}\n        />\n        <ChartTooltip\n          cursor={false}\n          content={<ChartTooltipContent indicator=\"dot\" />}\n        />\n        <Area\n          dataKey=\"mobile\"\n          type=\"natural\"\n          fill=\"var(--color-mobile)\"\n          fillOpacity={0.4}\n          stroke=\"var(--color-mobile)\"\n          stackId=\"a\"\n        />\n        <Area\n          dataKey=\"desktop\"\n          type=\"natural\"\n          fill=\"var(--color-desktop)\"\n          fillOpacity={0.4}\n          stroke=\"var(--color-desktop)\"\n          stackId=\"a\"\n        />\n      </AreaChart>\n    </ChartContainer>\n  ),\n};\n\n/**\n * Combine multiple Bar components to create a stacked bar chart.\n */\nexport const StackedBarChart: Story = {\n  args: {\n    config: multiSeriesConfig,\n  },\n  render: (args) => (\n    <ChartContainer {...args}>\n      <BarChart accessibilityLayer data={multiSeriesData}>\n        <CartesianGrid vertical={false} />\n        <XAxis\n          dataKey=\"month\"\n          tickLine={false}\n          tickMargin={10}\n          axisLine={false}\n          tickFormatter={(value) => value.slice(0, 3)}\n        />\n        <ChartTooltip\n          cursor={false}\n          content={<ChartTooltipContent indicator=\"dashed\" />}\n        />\n        <Bar dataKey=\"desktop\" fill=\"var(--color-desktop)\" radius={4} />\n        <Bar dataKey=\"mobile\" fill=\"var(--color-mobile)\" radius={4} />\n      </BarChart>\n    </ChartContainer>\n  ),\n};\n\n/**\n * Combine multiple Line components to create a single line chart.\n */\nexport const MultiLineChart: Story = {\n  args: {\n    config: multiSeriesConfig,\n  },\n  render: (args) => (\n    <ChartContainer {...args}>\n      <LineChart\n        accessibilityLayer\n        data={multiSeriesData}\n        margin={{\n          left: 12,\n          right: 12,\n        }}\n      >\n        <CartesianGrid vertical={false} />\n        <XAxis\n          dataKey=\"month\"\n          tickLine={false}\n          axisLine={false}\n          tickMargin={8}\n          tickFormatter={(value) => value.slice(0, 3)}\n        />\n        <ChartTooltip\n          cursor={false}\n          content={<ChartTooltipContent hideLabel />}\n        />\n        <Line\n          dataKey=\"desktop\"\n          type=\"natural\"\n          stroke=\"var(--color-desktop)\"\n          strokeWidth={2}\n          dot={false}\n        />\n        <Line\n          dataKey=\"mobile\"\n          type=\"natural\"\n          stroke=\"var(--color-mobile)\"\n          strokeWidth={2}\n          dot={false}\n        />\n      </LineChart>\n    </ChartContainer>\n  ),\n};\n\n/**\n * Combine Pie and Label components to create a doughnut chart.\n */\nexport const DoughnutChart: Story = {\n  args: {\n    config: singleSeriesConfig,\n  },\n  render: (args) => {\n    const totalVisitors = useMemo(() => {\n      return singleSeriesData.reduce((acc, curr) => acc + curr.visitors, 0);\n    }, []);\n    return (\n      <ChartContainer {...args}>\n        <PieChart>\n          <ChartTooltip\n            cursor={false}\n            content={<ChartTooltipContent hideLabel />}\n          />\n          <Pie\n            data={singleSeriesData}\n            dataKey=\"visitors\"\n            nameKey=\"browser\"\n            innerRadius={48}\n            strokeWidth={5}\n          >\n            <Label\n              content={({ viewBox }) => {\n                if (viewBox && \"cx\" in viewBox && \"cy\" in viewBox) {\n                  return (\n                    <text\n                      x={viewBox.cx}\n                      y={viewBox.cy}\n                      textAnchor=\"middle\"\n                      dominantBaseline=\"middle\"\n                    >\n                      <tspan\n                        x={viewBox.cx}\n                        y={viewBox.cy}\n                        className=\"fill-foreground font-bold text-3xl\"\n                      >\n                        {totalVisitors.toLocaleString()}\n                      </tspan>\n                      <tspan\n                        x={viewBox.cx}\n                        y={(viewBox.cy || 0) + 24}\n                        className=\"fill-muted-foreground\"\n                      >\n                        Visitors\n                      </tspan>\n                    </text>\n                  );\n                }\n              }}\n            />\n          </Pie>\n        </PieChart>\n      </ChartContainer>\n    );\n  },\n};\n",
      "type": "registry:component",
      "target": "@ui/chart.stories.tsx"
    },
    {
      "path": "registry/ui/checkbox-story/checkbox-base.stories.tsx",
      "content": "// Replace nextjs-vite with the name of your framework\nimport type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\n\nimport { Checkbox } from \"@/components/ui/checkbox\";\nimport { Label } from \"@/components/ui/label\";\nimport { expect, userEvent, within } from \"storybook/test\";\n\n/**\n * A control that allows the user to toggle between checked and not checked.\n */\nconst meta: Meta<typeof Checkbox> = {\n  title: \"ui/base/Checkbox\",\n  component: Checkbox,\n  tags: [\"autodocs\"],\n  argTypes: {},\n  args: {\n    id: \"terms\",\n    disabled: false,\n  },\n  render: (args) => (\n    <div className=\"flex space-x-2\">\n      <Checkbox {...args} />\n      <Label htmlFor={args.id}>Accept terms and conditions</Label>\n    </div>\n  ),\n  parameters: {\n    layout: \"centered\",\n  },\n} satisfies Meta<typeof Checkbox>;\n\nexport default meta;\n\ntype Story = StoryObj<typeof meta>;\n\n/**\n * The default form of the checkbox.\n */\nexport const Default: Story = {};\n\n/**\n * Use the `disabled` prop to disable the checkbox.\n */\nexport const Disabled: Story = {\n  args: {\n    id: \"disabled-terms\",\n    disabled: true,\n  },\n};\n\nexport const ShouldToggleCheck: Story = {\n  name: \"when the checkbox is clicked, should toggle between checked and not checked\",\n  tags: [\"!dev\", \"!autodocs\"],\n  play: async ({ canvasElement }) => {\n    const canvas = within(canvasElement);\n    const checkbox = await canvas.getByRole(\"checkbox\");\n    await userEvent.click(checkbox);\n    expect(checkbox).toBeChecked();\n    await userEvent.click(checkbox, { delay: 100 });\n    expect(checkbox).not.toBeChecked();\n    await userEvent.click(checkbox, { delay: 100 });\n    expect(checkbox).toBeChecked();\n  },\n};\n",
      "type": "registry:component",
      "target": "@ui/checkbox.stories.tsx"
    },
    {
      "path": "registry/ui/collapsible-story/collapsible-base.stories.tsx",
      "content": "// Replace nextjs-vite with the name of your framework\nimport type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\nimport { Info } from \"lucide-react\";\n\nimport {\n  Collapsible,\n  CollapsibleContent,\n  CollapsibleTrigger,\n} from \"@/components/ui/collapsible\";\nimport { expect, userEvent } from \"storybook/test\";\n\n/**\n * An interactive component which expands/collapses a panel.\n */\nconst meta = {\n  title: \"ui/base/Collapsible\",\n  component: Collapsible,\n  tags: [\"autodocs\"],\n  argTypes: {},\n  args: {\n    className: \"w-96\",\n    disabled: false,\n  },\n  render: (args) => (\n    <Collapsible {...args}>\n      <CollapsibleTrigger className=\"flex gap-2\">\n        <h3 className=\"font-semibold\">Can I use this in my project?</h3>\n        <Info className=\"size-6\" />\n      </CollapsibleTrigger>\n      <CollapsibleContent>\n        Yes. Free to use for personal and commercial projects. No attribution\n        required.\n      </CollapsibleContent>\n    </Collapsible>\n  ),\n  parameters: {\n    layout: \"centered\",\n  },\n} satisfies Meta<typeof Collapsible>;\n\nexport default meta;\n\ntype Story = StoryObj<typeof meta>;\n\n/**\n * The default form of the collapsible.\n */\nexport const Default: Story = {};\n\n/**\n * Use the `disabled` prop to disable the interaction.\n */\nexport const Disabled: Story = {\n  args: {\n    disabled: true,\n  },\n};\n\nexport const ShouldOpenClose: Story = {\n  name: \"when collapsable trigger is clicked, should show content\",\n  tags: [\"!dev\", \"!autodocs\"],\n  play: async ({ canvas, step }) => {\n    const trigger = await canvas.findByRole(\"button\");\n\n    await step(\"Open the collapsible\", async () => {\n      await userEvent.click(trigger, { delay: 100 });\n      expect(await canvas.queryByText(/yes/i, { exact: true })).toBeVisible();\n    });\n\n    await step(\"Close the collapsible\", async () => {\n      await userEvent.click(trigger, { delay: 100 });\n      expect(await canvas.queryByText(/yes/i, { exact: true })).toBeNull();\n    });\n  },\n};\n",
      "type": "registry:component",
      "target": "@ui/collapsible.stories.tsx"
    },
    {
      "path": "registry/ui/command-story/command-base.stories.tsx",
      "content": "// Replace nextjs-vite with the name of your framework\nimport type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\n\nimport {\n  Command,\n  CommandEmpty,\n  CommandGroup,\n  CommandInput,\n  CommandItem,\n  CommandList,\n  CommandSeparator,\n} from \"@/components/ui/command\";\nimport { expect, userEvent, within } from \"storybook/test\";\n\n/**\n * Fast, composable, unstyled command menu for React.\n */\nconst meta = {\n  title: \"ui/base/Command\",\n  component: Command,\n  tags: [\"autodocs\"],\n  argTypes: {},\n  args: {\n    className: \"rounded-lg w-96 border shadow-md\",\n  },\n  render: (args) => (\n    <Command {...args}>\n      <CommandInput placeholder=\"Type a command or search...\" />\n      <CommandList>\n        <CommandEmpty>No results found.</CommandEmpty>\n        <CommandGroup heading=\"Suggestions\">\n          <CommandItem>Calendar</CommandItem>\n          <CommandItem>Search Emoji</CommandItem>\n          <CommandItem disabled>Calculator</CommandItem>\n        </CommandGroup>\n        <CommandSeparator />\n        <CommandGroup heading=\"Settings\">\n          <CommandItem>Profile</CommandItem>\n          <CommandItem>Billing</CommandItem>\n          <CommandItem>Settings</CommandItem>\n        </CommandGroup>\n      </CommandList>\n    </Command>\n  ),\n  parameters: {\n    layout: \"centered\",\n  },\n} satisfies Meta<typeof Command>;\n\nexport default meta;\n\ntype Story = StoryObj<typeof meta>;\n\n/**\n * The default form of the command.\n */\nexport const Default: Story = {};\n\nexport const TypingInCombobox: Story = {\n  name: \"when typing into the combobox, should filter results\",\n  tags: [\"!dev\", \"!autodocs\"],\n  play: async ({ canvasElement }) => {\n    const canvas = within(canvasElement);\n    const input = canvas.getByRole(\"combobox\");\n\n    // Search for \"calendar\" which should return a single result\n    await userEvent.type(input, \"calen\", { delay: 100 });\n    expect(canvas.getAllByRole(\"option\", { name: /calendar/i })).toHaveLength(\n      1,\n    );\n\n    await userEvent.clear(input);\n\n    // Search for \"story\" which should return multiple results\n    await userEvent.type(input, \"se\", { delay: 100 });\n    expect(canvas.getAllByRole(\"option\").length).toBeGreaterThan(1);\n    expect(canvas.getAllByRole(\"option\", { name: /search/i })).toHaveLength(1);\n\n    await userEvent.clear(input);\n\n    // Search for \"story\" which should return no results\n    await userEvent.type(input, \"story\", { delay: 100 });\n    expect(canvas.queryAllByRole(\"option\", { hidden: false })).toHaveLength(0);\n    expect(canvas.getByText(/no results/i)).toBeVisible();\n  },\n};\n",
      "type": "registry:component",
      "target": "@ui/command.stories.tsx"
    },
    {
      "path": "registry/ui/context-menu-story/context-menu-base.stories.tsx",
      "content": "// Replace nextjs-vite with the name of your framework\nimport type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\n\nimport {\n  ContextMenu,\n  ContextMenuCheckboxItem,\n  ContextMenuContent,\n  ContextMenuItem,\n  ContextMenuLabel,\n  ContextMenuRadioGroup,\n  ContextMenuRadioItem,\n  ContextMenuSeparator,\n  ContextMenuShortcut,\n  ContextMenuSub,\n  ContextMenuSubContent,\n  ContextMenuSubTrigger,\n  ContextMenuTrigger,\n} from \"@/components/ui/context-menu\";\nimport { expect, userEvent, within } from \"storybook/test\";\n\n/**\n * Displays a menu to the user — such as a set of actions or functions —\n * triggered by a button.\n */\nconst meta = {\n  title: \"ui/base/ContextMenu\",\n  component: ContextMenu,\n  tags: [\"autodocs\"],\n  argTypes: {},\n  args: {},\n  render: (args) => (\n    <ContextMenu {...args}>\n      <ContextMenuTrigger className=\"flex h-48 w-96 items-center justify-center rounded-md border border-dashed bg-accent text-sm\">\n        Right click here\n      </ContextMenuTrigger>\n      <ContextMenuContent className=\"w-32\">\n        <ContextMenuItem>Profile</ContextMenuItem>\n        <ContextMenuItem>Billing</ContextMenuItem>\n        <ContextMenuItem>Team</ContextMenuItem>\n        <ContextMenuItem>Subscription</ContextMenuItem>\n      </ContextMenuContent>\n    </ContextMenu>\n  ),\n  parameters: {\n    layout: \"centered\",\n  },\n} satisfies Meta<typeof ContextMenu>;\n\nexport default meta;\n\ntype Story = StoryObj<typeof meta>;\n\n/**\n * The default form of the context menu.\n */\nexport const Default: Story = {};\n\n/**\n * A context menu with shortcuts.\n */\nexport const WithShortcuts: Story = {\n  render: (args) => (\n    <ContextMenu {...args}>\n      <ContextMenuTrigger className=\"flex h-48 w-96 items-center justify-center rounded-md border border-dashed bg-accent text-sm\">\n        Right click here\n      </ContextMenuTrigger>\n      <ContextMenuContent className=\"w-32\">\n        <ContextMenuItem>\n          Back\n          <ContextMenuShortcut>⌘[</ContextMenuShortcut>\n        </ContextMenuItem>\n        <ContextMenuItem disabled>\n          Forward\n          <ContextMenuShortcut>⌘]</ContextMenuShortcut>\n        </ContextMenuItem>\n        <ContextMenuItem>\n          Reload\n          <ContextMenuShortcut>⌘R</ContextMenuShortcut>\n        </ContextMenuItem>\n      </ContextMenuContent>\n    </ContextMenu>\n  ),\n};\n\n/**\n * A context menu with a submenu.\n */\nexport const WithSubmenu: Story = {\n  render: (args) => (\n    <ContextMenu {...args}>\n      <ContextMenuTrigger className=\"flex h-48 w-96 items-center justify-center rounded-md border border-dashed bg-accent text-sm\">\n        Right click here\n      </ContextMenuTrigger>\n      <ContextMenuContent className=\"w-32\">\n        <ContextMenuItem>\n          New Tab\n          <ContextMenuShortcut>⌘N</ContextMenuShortcut>\n        </ContextMenuItem>\n        <ContextMenuSub>\n          <ContextMenuSubTrigger>More Tools</ContextMenuSubTrigger>\n          <ContextMenuSubContent>\n            <ContextMenuItem>\n              Save Page As...\n              <ContextMenuShortcut>⇧⌘S</ContextMenuShortcut>\n            </ContextMenuItem>\n            <ContextMenuItem>Create Shortcut...</ContextMenuItem>\n            <ContextMenuItem>Name Window...</ContextMenuItem>\n            <ContextMenuSeparator />\n            <ContextMenuItem>Developer Tools</ContextMenuItem>\n          </ContextMenuSubContent>\n        </ContextMenuSub>\n      </ContextMenuContent>\n    </ContextMenu>\n  ),\n};\n\n/**\n * A context menu with checkboxes.\n */\nexport const WithCheckboxes: Story = {\n  render: (args) => (\n    <ContextMenu {...args}>\n      <ContextMenuTrigger className=\"flex h-48 w-96 items-center justify-center rounded-md border border-dashed bg-accent text-sm\">\n        Right click here\n      </ContextMenuTrigger>\n      <ContextMenuContent className=\"w-64\">\n        <ContextMenuCheckboxItem checked>\n          Show Comments\n          <ContextMenuShortcut>⌘⇧C</ContextMenuShortcut>\n        </ContextMenuCheckboxItem>\n        <ContextMenuCheckboxItem>Show Preview</ContextMenuCheckboxItem>\n      </ContextMenuContent>\n    </ContextMenu>\n  ),\n};\n\n/**\n * A context menu with a radio group.\n */\nexport const WithRadioGroup: Story = {\n  render: (args) => (\n    <ContextMenu {...args}>\n      <ContextMenuTrigger className=\"flex h-48 w-96 items-center justify-center rounded-md border border-dashed bg-accent text-sm\">\n        Right click here\n      </ContextMenuTrigger>\n      <ContextMenuContent className=\"w-64\">\n        <ContextMenuRadioGroup value=\"light\">\n          <ContextMenuLabel inset>Theme</ContextMenuLabel>\n          <ContextMenuRadioItem value=\"light\">Light</ContextMenuRadioItem>\n          <ContextMenuRadioItem value=\"dark\">Dark</ContextMenuRadioItem>\n        </ContextMenuRadioGroup>\n      </ContextMenuContent>\n    </ContextMenu>\n  ),\n};\n\nexport const ShouldOpenClose: Story = {\n  name: \"when right-clicking the trigger area, the menu appears and can be interacted with\",\n  tags: [\"!dev\", \"!autodocs\"],\n  play: async ({ canvasElement, canvas, step }) => {\n    const canvasBody = within(canvasElement.ownerDocument.body);\n\n    step(\"Right-click on the trigger area\", async () => {\n      await userEvent.pointer({\n        keys: \"[MouseRight>]\",\n        target: await canvas.findByText(/click here/i),\n        coords: {\n          x: canvasElement.clientWidth / 2,\n          y: canvasElement.clientHeight / 2,\n        },\n      });\n    });\n    expect(await canvasBody.findByRole(\"menu\")).toBeInTheDocument();\n    const items = await canvasBody.findAllByRole(\"menuitem\");\n    expect(items).toHaveLength(4);\n\n    step(\"Click the first menu item\", async () => {\n      await userEvent.click(items[0], { delay: 100 });\n    });\n  },\n};\n",
      "type": "registry:component",
      "target": "@ui/context-menu.stories.tsx"
    },
    {
      "path": "registry/ui/date-picker-story/date-picker-base.stories.tsx",
      "content": "import { expect, userEvent, waitFor, within } from \"storybook/test\";\n// Replace nextjs-vite with the name of your framework\nimport type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\n\nimport { Button } from \"@/components/ui/button\";\nimport { Calendar } from \"@/components/ui/calendar\";\nimport { Input } from \"@/components/ui/input\";\nimport { Label } from \"@/components/ui/label\";\nimport {\n  Popover,\n  PopoverContent,\n  PopoverTrigger,\n} from \"@/components/ui/popover\";\nimport { CalendarIcon, ChevronDownIcon } from \"lucide-react\";\nimport { useState } from \"react\";\nimport { action } from \"storybook/actions\";\n\n/**\n * A window overlaid on either the primary window or another dialog window,\n * rendering the content underneath inert.\n */\nconst meta = {\n  title: \"ui/base/DatePicker\",\n  component: Calendar,\n  tags: [\"autodocs\"],\n  argTypes: {},\n  parameters: {\n    layout: \"centered\",\n  },\n} satisfies Meta<typeof Calendar>;\n\nexport default meta;\n\ntype Story = StoryObj<typeof meta>;\n\n/**\n * Combination of the calendar and a button that opens a popover.\n */\nexport const WithPopover: Story = {\n  args: {\n    captionLayout: \"dropdown\",\n  },\n\n  render: (args) => {\n    const [open, setOpen] = useState(false);\n    const [date, setDate] = useState<Date | undefined>(undefined);\n    return (\n      <div className=\"flex flex-col gap-3\">\n        <Label htmlFor=\"date\" className=\"px-1\">\n          Date of birth\n        </Label>\n        <Popover open={open} onOpenChange={setOpen}>\n          <PopoverTrigger\n            render={\n              <Button\n                variant=\"outline\"\n                id=\"date\"\n                className=\"w-48 justify-between font-normal\"\n              />\n            }\n          >\n            {date ? date.toLocaleDateString() : \"Select date\"}\n            <ChevronDownIcon />\n          </PopoverTrigger>\n          <PopoverContent className=\"w-auto overflow-hidden p-0\" align=\"start\">\n            <Calendar\n              {...args}\n              mode=\"single\"\n              selected={date}\n              onSelect={(date) => {\n                setDate(date);\n                setOpen(false);\n                action(\"date selected\")(date);\n              }}\n            />\n          </PopoverContent>\n        </Popover>\n      </div>\n    );\n  },\n};\n\nexport const ShouldOpenPopover: Story = {\n  ...WithPopover,\n  name: \"when clicking the button, should open the popover to select a date\",\n  tags: [\"!dev\", \"!autodocs\"],\n  play: async ({ canvasElement, step }) => {\n    const canvas = within(canvasElement.ownerDocument.body);\n    await step(\"Open the popover\", async () => {\n      await userEvent.click(\n        await canvas.findByRole(\"button\", { name: \"Date of birth\" }),\n      );\n      await waitFor(() =>\n        expect(\n          canvasElement.ownerDocument.body.querySelector(\".rdp-root\"),\n        ).toBeVisible(),\n      );\n    });\n    await step(\"Select a date\", async () => {\n      const dateButtons = await canvas.findAllByRole(\"button\", {\n        name: /1st/i,\n      });\n      await userEvent.click(dateButtons[0]);\n    });\n  },\n};\n\nconst formatDate = (date: Date | undefined) => {\n  return date\n    ? date.toLocaleDateString(\"en-US\", {\n        day: \"2-digit\",\n        month: \"long\",\n        year: \"numeric\",\n      })\n    : \"\";\n};\n\nconst isValidDate = (date: Date | undefined) =>\n  date ? !Number.isNaN(date.getTime()) : false;\n\n/**\n * Combination of the calendar and an input field that allows typing a date.\n */\nexport const WithInput: Story = {\n  args: {\n    captionLayout: \"dropdown\",\n  },\n\n  render: (args) => {\n    const [open, setOpen] = useState(false);\n    const [date, setDate] = useState<Date | undefined>(new Date(\"2025-06-01\"));\n    const [month, setMonth] = useState<Date | undefined>(date);\n    const [value, setValue] = useState(formatDate(date));\n\n    return (\n      <div className=\"flex flex-col gap-3\">\n        <Label htmlFor=\"date\" className=\"px-1\">\n          Subscription Date\n        </Label>\n        <div className=\"relative flex gap-2\">\n          <Input\n            id=\"date\"\n            value={value}\n            placeholder=\"June 01, 2025\"\n            className=\"bg-background pr-10\"\n            onChange={(e) => {\n              const date = new Date(e.target.value);\n              setValue(e.target.value);\n              if (isValidDate(date)) {\n                setDate(date);\n                setMonth(date);\n                action(\"date input changed\")(date);\n              }\n            }}\n            onKeyDown={(e) => {\n              if (e.key === \"ArrowDown\") {\n                e.preventDefault();\n                setOpen(true);\n              }\n            }}\n          />\n          <Popover open={open} onOpenChange={setOpen}>\n            <PopoverTrigger\n              render={\n                <Button\n                  id=\"date-picker\"\n                  variant=\"ghost\"\n                  className=\"absolute top-1/2 right-2 size-6 -translate-y-1/2\"\n                />\n              }\n            >\n              <CalendarIcon className=\"size-3.5\" />\n              <span className=\"sr-only\">Select date</span>\n            </PopoverTrigger>\n            <PopoverContent\n              className=\"w-auto overflow-hidden p-0\"\n              align=\"end\"\n              alignOffset={-8}\n              sideOffset={10}\n            >\n              <Calendar\n                {...args}\n                mode=\"single\"\n                selected={date}\n                month={month}\n                onMonthChange={setMonth}\n                onSelect={(date) => {\n                  setDate(date);\n                  setValue(formatDate(date));\n                  setOpen(false);\n                  action(\"date selected\")(date);\n                }}\n              />\n            </PopoverContent>\n          </Popover>\n        </div>\n      </div>\n    );\n  },\n};\n\nexport const ShouldEnterTextDate: Story = {\n  ...WithInput,\n  name: \"when typing a valid date, should update the input and close the calendar\",\n  tags: [\"!dev\", \"!autodocs\"],\n  play: async ({ canvasElement, step }) => {\n    const canvas = within(canvasElement.ownerDocument.body);\n    const input = await canvas.findByRole(\"textbox\", {\n      name: \"Subscription Date\",\n    });\n    await step(\"type a date\", async () => {\n      await userEvent.click(input);\n      await userEvent.clear(input);\n      await userEvent.type(input, \"July 21, 1999\");\n      await userEvent.keyboard(\"{enter}\");\n      expect(input).toHaveValue(\"July 21, 1999\");\n    });\n\n    await step(\"check the calendar\", async () => {\n      await userEvent.click(\n        await canvas.findByRole(\"button\", { name: \"Select date\" }),\n      );\n      await waitFor(() =>\n        expect(\n          canvas.queryByRole(\"button\", {\n            name: \"Wednesday, July 21st, 1999, selected\",\n          }),\n        ).toBeVisible(),\n      );\n    });\n  },\n};\n\n/**\n * Combination of the calendar and an input field that allows changing the time.\n */\nexport const WithDateTime: Story = {\n  args: {\n    captionLayout: \"dropdown\",\n  },\n\n  render: (args) => {\n    const [open, setOpen] = useState(false);\n    const [date, setDate] = useState<Date | undefined>(undefined);\n    return (\n      <div className=\"flex gap-4\">\n        <div className=\"flex flex-col gap-3\">\n          <Label htmlFor=\"date-picker\" className=\"px-1\">\n            Date\n          </Label>\n          <Popover open={open} onOpenChange={setOpen}>\n            <PopoverTrigger\n              render={\n                <Button\n                  variant=\"outline\"\n                  id=\"date-picker\"\n                  className=\"w-32 justify-between font-normal\"\n                />\n              }\n            >\n              {date ? date.toLocaleDateString() : \"Select date\"}\n              <ChevronDownIcon />\n            </PopoverTrigger>\n            <PopoverContent\n              className=\"w-auto overflow-hidden p-0\"\n              align=\"start\"\n            >\n              <Calendar\n                {...args}\n                mode=\"single\"\n                selected={date}\n                onSelect={(date) => {\n                  setDate(date);\n                  setOpen(false);\n                  action(\"date selected\")(date);\n                }}\n              />\n            </PopoverContent>\n          </Popover>\n        </div>\n        <div className=\"flex flex-col gap-3\">\n          <Label htmlFor=\"time-picker\" className=\"px-1\">\n            Time\n          </Label>\n          <Input\n            type=\"time\"\n            id=\"time-picker\"\n            step=\"1\"\n            disabled={!date}\n            defaultValue=\"10:30:00\"\n            onChange={(e) => {\n              if (!date) {\n                return;\n              }\n              const [hours, minutes, seconds] = e.target.value\n                .split(\":\")\n                .map(Number);\n              date.setHours(hours, minutes, seconds);\n              setDate(date);\n              action(\"time selected\")(date);\n            }}\n            className=\"appearance-none bg-background [&::-webkit-calendar-picker-indicator]:hidden [&::-webkit-calendar-picker-indicator]:appearance-none\"\n          />\n        </div>\n      </div>\n    );\n  },\n};\n\nexport const ShouldOpenCalendar: Story = {\n  ...WithDateTime,\n  name: \"when clicking the date button, should open the calendar to select a date\",\n  tags: [\"!dev\", \"!autodocs\"],\n  play: async ({ canvasElement, step }) => {\n    const canvas = within(canvasElement.ownerDocument.body);\n\n    await step(\"Open the date popover\", async () => {\n      const dateInput = await canvas.findByLabelText(\"Date\");\n      await userEvent.click(dateInput);\n      await waitFor(() =>\n        expect(\n          canvas.queryAllByRole(\"button\", { name: /1st/i }).at(0),\n        ).toBeVisible(),\n      );\n    });\n\n    const dateButtons = await canvas.findAllByRole(\"button\", { name: /1st/i });\n    await userEvent.click(dateButtons[0]);\n\n    await step(\"type a time\", async () => {\n      const timeInput = await canvas.findByLabelText(\"Time\");\n      await userEvent.click(timeInput);\n      await userEvent.type(timeInput, \"1\");\n    });\n  },\n};\n",
      "type": "registry:component",
      "target": "@ui/date-picker.stories.tsx"
    },
    {
      "path": "registry/ui/dialog-story/dialog-base.stories.tsx",
      "content": "import { expect, userEvent, waitFor, within } from \"storybook/test\";\n// Replace nextjs-vite with the name of your framework\nimport type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\n\nimport {\n  Dialog,\n  DialogClose,\n  DialogContent,\n  DialogDescription,\n  DialogFooter,\n  DialogHeader,\n  DialogTitle,\n  DialogTrigger,\n} from \"@/components/ui/dialog\";\n\n/**\n * A window overlaid on either the primary window or another dialog window,\n * rendering the content underneath inert.\n */\nconst meta = {\n  title: \"ui/base/Dialog\",\n  component: Dialog,\n  tags: [\"autodocs\"],\n  argTypes: {},\n  render: (args) => (\n    <Dialog {...args}>\n      <DialogTrigger>Open</DialogTrigger>\n      <DialogContent>\n        <DialogHeader>\n          <DialogTitle>Are you absolutely sure?</DialogTitle>\n          <DialogDescription>\n            This action cannot be undone. This will permanently delete your\n            account and remove your data from our servers.\n          </DialogDescription>\n        </DialogHeader>\n        <DialogFooter className=\"gap-4\">\n          <DialogClose className=\"hover:underline\">Cancel</DialogClose>\n          <DialogClose className=\"rounded bg-primary px-4 py-2 text-primary-foreground\">\n            Continue\n          </DialogClose>\n        </DialogFooter>\n      </DialogContent>\n    </Dialog>\n  ),\n  parameters: {\n    layout: \"centered\",\n  },\n} satisfies Meta<typeof Dialog>;\n\nexport default meta;\n\ntype Story = StoryObj<typeof meta>;\n\n/**\n * The default form of the dialog.\n */\nexport const Default: Story = {};\n\nexport const ShouldOpenCloseWithContinue: Story = {\n  name: \"when clicking Continue button, should close the dialog\",\n  tags: [\"!dev\", \"!autodocs\"],\n  play: async ({ canvasElement, step }) => {\n    const canvasBody = within(canvasElement.ownerDocument.body);\n\n    await step(\"Open the dialog\", async () => {\n      await userEvent.click(\n        await canvasBody.findByRole(\"button\", { name: /open/i }),\n      );\n      const dialog = await canvasBody.findByRole(\"dialog\");\n      await waitFor(() => {\n        expect(dialog).toHaveAttribute(\"data-open\");\n      });\n    });\n\n    await step(\"Close the dialog\", async () => {\n      await userEvent.click(\n        await canvasBody.findByRole(\"button\", { name: /continue/i }),\n      );\n      await waitFor(() => {\n        expect(canvasBody.queryByRole(\"dialog\")).not.toBeInTheDocument();\n      });\n    });\n  },\n};\n\nexport const ShouldOpenCloseWithCancel: Story = {\n  name: \"when clicking Cancel button, should close the dialog\",\n  tags: [\"!dev\", \"!autodocs\"],\n  play: async ({ canvasElement, step }) => {\n    const canvasBody = within(canvasElement.ownerDocument.body);\n\n    await step(\"Open the dialog\", async () => {\n      await userEvent.click(\n        await canvasBody.findByRole(\"button\", { name: /open/i }),\n      );\n      const dialog = await canvasBody.findByRole(\"dialog\");\n      await waitFor(() => {\n        expect(dialog).toHaveAttribute(\"data-open\");\n      });\n    });\n\n    await step(\"Close the dialog\", async () => {\n      await userEvent.click(\n        await canvasBody.findByRole(\"button\", { name: /cancel/i }),\n      );\n      await waitFor(() => {\n        expect(canvasBody.queryByRole(\"dialog\")).not.toBeInTheDocument();\n      });\n    });\n  },\n};\n\nexport const ShouldOpenCloseCross: Story = {\n  name: \"when clicking Close icon, should close the dialog\",\n  tags: [\"!dev\", \"!autodocs\"],\n  play: async ({ canvasElement, step }) => {\n    const canvasBody = within(canvasElement.ownerDocument.body);\n\n    await step(\"Open the dialog\", async () => {\n      await userEvent.click(\n        await canvasBody.findByRole(\"button\", { name: /open/i }),\n      );\n      const dialog = await canvasBody.findByRole(\"dialog\");\n      await waitFor(() => {\n        expect(dialog).toHaveAttribute(\"data-open\");\n      });\n    });\n\n    await step(\"Close the dialog\", async () => {\n      await userEvent.click(\n        await canvasBody.findByRole(\"button\", { name: /close/i }),\n      );\n      await waitFor(() => {\n        expect(canvasBody.queryByRole(\"dialog\")).not.toBeInTheDocument();\n      });\n    });\n  },\n};\n",
      "type": "registry:component",
      "target": "@ui/dialog.stories.tsx"
    },
    {
      "path": "registry/ui/drawer-story/drawer-base.stories.tsx",
      "content": "// Replace nextjs-vite with the name of your framework\nimport type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\n\nimport {\n  Drawer,\n  DrawerClose,\n  DrawerContent,\n  DrawerDescription,\n  DrawerFooter,\n  DrawerHeader,\n  DrawerTitle,\n  DrawerTrigger,\n} from \"@/components/ui/drawer\";\nimport { expect, fn, userEvent, within } from \"storybook/test\";\n\n/**\n * A drawer component for React.\n */\nconst meta = {\n  title: \"ui/base/Drawer\",\n  component: Drawer,\n  tags: [\"autodocs\"],\n  args: {\n    onOpenChange: fn(),\n    onClose: fn(),\n    onAnimationEnd: fn(),\n  },\n  render: (args) => (\n    <Drawer {...args}>\n      <DrawerTrigger>Open</DrawerTrigger>\n      <DrawerContent>\n        <DrawerHeader>\n          <DrawerTitle>Are you sure absolutely sure?</DrawerTitle>\n          <DrawerDescription>This action cannot be undone.</DrawerDescription>\n        </DrawerHeader>\n        <DrawerFooter>\n          <DrawerClose className=\"rounded bg-primary px-4 py-2 text-primary-foreground\">\n            Submit\n          </DrawerClose>\n          <DrawerClose className=\"hover:underline\">Cancel</DrawerClose>\n        </DrawerFooter>\n      </DrawerContent>\n    </Drawer>\n  ),\n  parameters: {\n    layout: \"centered\",\n  },\n} satisfies Meta<typeof Drawer>;\n\nexport default meta;\n\ntype Story = StoryObj<typeof meta>;\n\n/**\n * The default form of the drawer.\n */\nexport const Default: Story = {};\n\nexport const ShouldOpenCloseWithSubmit: Story = {\n  name: \"when clicking Submit button, should close the drawer\",\n  tags: [\"!dev\", \"!autodocs\"],\n  play: async ({ args, canvasElement, step }) => {\n    const canvasBody = within(canvasElement.ownerDocument.body);\n\n    await step(\"Open the drawer\", async () => {\n      await userEvent.click(\n        await canvasBody.findByRole(\"button\", { name: /open/i }),\n      );\n      await expect(args.onOpenChange).toHaveBeenCalled();\n\n      const dialog = await canvasBody.findByRole(\"dialog\");\n      expect(dialog).toBeInTheDocument();\n      expect(dialog).toHaveAttribute(\"data-state\", \"open\");\n    });\n\n    await step(\"Close the drawer\", async () => {\n      await userEvent.click(\n        await canvasBody.findByRole(\"button\", { name: /submit/i }),\n        { delay: 100 },\n      );\n      await expect(args.onClose).toHaveBeenCalled();\n      expect(await canvasBody.findByRole(\"dialog\")).toHaveAttribute(\n        \"data-state\",\n        \"closed\",\n      );\n    });\n  },\n};\n\nexport const ShouldOpenCloseWithCancel: Story = {\n  name: \"when clicking Cancel button, should close the drawer\",\n  tags: [\"!dev\", \"!autodocs\"],\n  play: async ({ args, canvasElement, step }) => {\n    const canvasBody = within(canvasElement.ownerDocument.body);\n\n    await step(\"Open the drawer\", async () => {\n      await userEvent.click(\n        await canvasBody.findByRole(\"button\", { name: /open/i }),\n      );\n      await expect(args.onOpenChange).toHaveBeenCalled();\n\n      const dialog = await canvasBody.findByRole(\"dialog\");\n      expect(dialog).toBeInTheDocument();\n      expect(dialog).toHaveAttribute(\"data-state\", \"open\");\n    });\n\n    await step(\"Close the drawer\", async () => {\n      await userEvent.click(\n        await canvasBody.findByRole(\"button\", { name: /cancel/i }),\n        { delay: 100 },\n      );\n      await expect(args.onClose).toHaveBeenCalled();\n      expect(await canvasBody.findByRole(\"dialog\")).toHaveAttribute(\n        \"data-state\",\n        \"closed\",\n      );\n    });\n  },\n};\n",
      "type": "registry:component",
      "target": "@ui/drawer.stories.tsx"
    },
    {
      "path": "registry/ui/dropdown-menu-story/dropdown-menu-base.stories.tsx",
      "content": "import { expect, userEvent, waitFor, within } from \"storybook/test\";\n// Replace nextjs-vite with the name of your framework\nimport type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\nimport { Mail, Plus, PlusCircle, Search, UserPlus } from \"lucide-react\";\n\nimport {\n  DropdownMenu,\n  DropdownMenuCheckboxItem,\n  DropdownMenuContent,\n  DropdownMenuGroup,\n  DropdownMenuItem,\n  DropdownMenuLabel,\n  DropdownMenuPortal,\n  DropdownMenuRadioGroup,\n  DropdownMenuRadioItem,\n  DropdownMenuSeparator,\n  DropdownMenuShortcut,\n  DropdownMenuSub,\n  DropdownMenuSubContent,\n  DropdownMenuSubTrigger,\n  DropdownMenuTrigger,\n} from \"@/components/ui/dropdown-menu\";\n\n/**\n * Displays a menu to the user — such as a set of actions or functions —\n * triggered by a button.\n */\nconst meta = {\n  title: \"ui/base/DropdownMenu\",\n  component: DropdownMenu,\n  tags: [\"autodocs\"],\n  argTypes: {},\n  render: (args) => (\n    <DropdownMenu {...args}>\n      <DropdownMenuTrigger>Open</DropdownMenuTrigger>\n      <DropdownMenuContent className=\"w-44\">\n        <DropdownMenuGroup>\n          <DropdownMenuLabel>My Account</DropdownMenuLabel>\n          <DropdownMenuSeparator />\n          <DropdownMenuItem>Profile</DropdownMenuItem>\n          <DropdownMenuItem>Billing</DropdownMenuItem>\n          <DropdownMenuItem>Team</DropdownMenuItem>\n          <DropdownMenuItem>Subscription</DropdownMenuItem>\n        </DropdownMenuGroup>\n      </DropdownMenuContent>\n    </DropdownMenu>\n  ),\n  parameters: {\n    layout: \"centered\",\n  },\n} satisfies Meta<typeof DropdownMenu>;\n\nexport default meta;\n\ntype Story = StoryObj<typeof meta>;\n\n/**\n * The default form of the dropdown menu.\n */\nexport const Default: Story = {};\n\n/**\n * A dropdown menu with shortcuts.\n */\nexport const WithShortcuts: Story = {\n  render: (args) => (\n    <DropdownMenu {...args}>\n      <DropdownMenuTrigger>Open</DropdownMenuTrigger>\n      <DropdownMenuContent className=\"w-44\">\n        <DropdownMenuGroup>\n          <DropdownMenuLabel>Controls</DropdownMenuLabel>\n          <DropdownMenuItem>\n            Back\n            <DropdownMenuShortcut>⌘[</DropdownMenuShortcut>\n          </DropdownMenuItem>\n          <DropdownMenuItem disabled>\n            Forward\n            <DropdownMenuShortcut>⌘]</DropdownMenuShortcut>\n          </DropdownMenuItem>\n        </DropdownMenuGroup>\n      </DropdownMenuContent>\n    </DropdownMenu>\n  ),\n};\n\n/**\n * A dropdown menu with submenus.\n */\nexport const WithSubmenus: Story = {\n  render: (args) => (\n    <DropdownMenu {...args}>\n      <DropdownMenuTrigger>Open</DropdownMenuTrigger>\n      <DropdownMenuContent className=\"w-44\">\n        <DropdownMenuItem>\n          <Search className=\"mr-2 size-4\" />\n          <span>Search</span>\n        </DropdownMenuItem>\n        <DropdownMenuSeparator />\n        <DropdownMenuGroup>\n          <DropdownMenuItem>\n            <Plus className=\"mr-2 size-4\" />\n            <span>New Team</span>\n            <DropdownMenuShortcut>⌘+T</DropdownMenuShortcut>\n          </DropdownMenuItem>\n          <DropdownMenuSub>\n            <DropdownMenuSubTrigger>\n              <UserPlus className=\"mr-2 size-4\" />\n              <span>Invite users</span>\n            </DropdownMenuSubTrigger>\n            <DropdownMenuPortal>\n              <DropdownMenuSubContent>\n                <DropdownMenuItem>\n                  <Mail className=\"mr-2 size-4\" />\n                  <span>Email</span>\n                </DropdownMenuItem>\n                <DropdownMenuSeparator />\n                <DropdownMenuItem>\n                  <PlusCircle className=\"mr-2 size-4\" />\n                  <span>More...</span>\n                </DropdownMenuItem>\n              </DropdownMenuSubContent>\n            </DropdownMenuPortal>\n          </DropdownMenuSub>\n        </DropdownMenuGroup>\n      </DropdownMenuContent>\n    </DropdownMenu>\n  ),\n};\n\n/**\n * A dropdown menu with radio items.\n */\nexport const WithRadioItems: Story = {\n  render: (args) => (\n    <DropdownMenu {...args}>\n      <DropdownMenuTrigger>Open</DropdownMenuTrigger>\n      <DropdownMenuContent className=\"w-44\">\n        <DropdownMenuGroup>\n          <DropdownMenuLabel inset>Status</DropdownMenuLabel>\n          <DropdownMenuRadioGroup value=\"warning\">\n            <DropdownMenuRadioItem value=\"info\">Info</DropdownMenuRadioItem>\n            <DropdownMenuRadioItem value=\"warning\">\n              Warning\n            </DropdownMenuRadioItem>\n            <DropdownMenuRadioItem value=\"error\">Error</DropdownMenuRadioItem>\n          </DropdownMenuRadioGroup>\n        </DropdownMenuGroup>\n      </DropdownMenuContent>\n    </DropdownMenu>\n  ),\n};\n\n/**\n * A dropdown menu with checkboxes.\n */\nexport const WithCheckboxes: Story = {\n  render: (args) => (\n    <DropdownMenu {...args}>\n      <DropdownMenuTrigger>Open</DropdownMenuTrigger>\n      <DropdownMenuContent className=\"w-44\">\n        <DropdownMenuCheckboxItem checked>\n          Autosave\n          <DropdownMenuShortcut>⌘S</DropdownMenuShortcut>\n        </DropdownMenuCheckboxItem>\n        <DropdownMenuCheckboxItem>Show Comments</DropdownMenuCheckboxItem>\n      </DropdownMenuContent>\n    </DropdownMenu>\n  ),\n};\n\nexport const ShouldOpenClose: Story = {\n  name: \"when clicking an item, should close the dropdown menu\",\n  tags: [\"!dev\", \"!autodocs\"],\n  play: async ({ canvasElement, step }) => {\n    const body = within(canvasElement.ownerDocument.body);\n\n    await step(\"Open the dropdown menu\", async () => {\n      await userEvent.click(await body.findByRole(\"button\", { name: /open/i }));\n      expect(await body.findByRole(\"menu\")).toBeInTheDocument();\n    });\n\n    await step(\"Click the first menu item\", async () => {\n      const items = await body.findAllByRole(\"menuitem\");\n      expect(items).toHaveLength(4);\n      await userEvent.click(items[0], { delay: 100 });\n      await waitFor(() => {\n        expect(body.queryByRole(\"menu\")).not.toBeInTheDocument();\n      });\n    });\n  },\n};\n",
      "type": "registry:component",
      "target": "@ui/dropdown-menu.stories.tsx"
    },
    {
      "path": "registry/ui/form-story/form-react-hook-base.stories.tsx",
      "content": "import { zodResolver } from \"@hookform/resolvers/zod\";\nimport type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\nimport type { ComponentProps } from \"react\";\nimport { Controller, useForm } from \"react-hook-form\";\nimport { action } from \"storybook/actions\";\nimport { expect, userEvent } from \"storybook/test\";\nimport * as z from \"zod\";\n\nimport { Button } from \"@/components/ui/button\";\nimport {\n  Field,\n  FieldDescription,\n  FieldError,\n  FieldGroup,\n  FieldLabel,\n} from \"@/components/ui/field\";\nimport { Input } from \"@/components/ui/input\";\n\nconst formSchema = z.object({\n  username: z.string().min(6, {\n    message: \"Username must be at least 6 characters.\",\n  }),\n});\n\ntype ProfileFormValues = z.infer<typeof formSchema>;\n\nfunction ReactHookFormProfileForm(props: ComponentProps<\"form\">) {\n  const form = useForm<ProfileFormValues>({\n    resolver: zodResolver(formSchema),\n    defaultValues: {\n      username: \"\",\n    },\n  });\n  function onSubmit(values: ProfileFormValues) {\n    action(\"onSubmit\")(values);\n  }\n  return (\n    <form\n      onSubmit={form.handleSubmit(onSubmit)}\n      className=\"space-y-8\"\n      {...props}\n    >\n      <FieldGroup>\n        <Controller\n          control={form.control}\n          name=\"username\"\n          render={({ field, fieldState }) => (\n            <Field data-invalid={fieldState.invalid}>\n              <FieldLabel htmlFor=\"form-rhf-username\">Username</FieldLabel>\n              <Input\n                id=\"form-rhf-username\"\n                placeholder=\"username\"\n                aria-invalid={fieldState.invalid}\n                {...field}\n              />\n              <FieldDescription>\n                This is your public display name.\n              </FieldDescription>\n              {fieldState.invalid && <FieldError errors={[fieldState.error]} />}\n            </Field>\n          )}\n        />\n      </FieldGroup>\n      <Button type=\"submit\">Submit</Button>\n    </form>\n  );\n}\n\n/**\n * Building forms with React Hook Form and Zod.\n */\nconst meta: Meta<typeof ReactHookFormProfileForm> = {\n  title: \"ui/base/Form (React Hook Form)\",\n  component: ReactHookFormProfileForm,\n  tags: [\"autodocs\"],\n  argTypes: {},\n} satisfies Meta<typeof ReactHookFormProfileForm>;\n\nexport default meta;\n\ntype Story = StoryObj<typeof meta>;\n\n/**\n * The default form of the form.\n */\nexport const Default: Story = {};\n\nexport const ShouldSucceedWhenValidInput: Story = {\n  name: \"when typing a valid username, should not show an error message\",\n  tags: [\"!dev\", \"!autodocs\"],\n  play: async ({ canvas, step }) => {\n    await step(\"Type a valid username\", async () => {\n      await userEvent.type(\n        await canvas.findByRole(\"textbox\", { name: /username/i }),\n        \"mockuser\",\n      );\n    });\n\n    await step(\"Click the submit button\", async () => {\n      await userEvent.click(\n        await canvas.findByRole(\"button\", { name: /submit/i }),\n      );\n      expect(\n        await canvas.queryByText(/username must be at least 6 characters/i, {\n          exact: true,\n        }),\n      ).toBeNull();\n    });\n  },\n};\n\nexport const ShouldShowErrorWhenInvalidInput: Story = {\n  name: \"when typing a short username, should show an error message\",\n  tags: [\"!dev\", \"!autodocs\"],\n  play: async ({ canvas, step }) => {\n    await step(\"Type a short username\", async () => {\n      await userEvent.type(\n        await canvas.findByRole(\"textbox\", { name: /username/i }),\n        \"fail\",\n      );\n    });\n\n    await step(\"Click the submit button\", async () => {\n      await userEvent.click(\n        await canvas.findByRole(\"button\", { name: /submit/i }),\n      );\n      expect(\n        await canvas.queryByText(/username must be at least 6 characters/i, {\n          exact: true,\n        }),\n      ).toBeVisible();\n    });\n  },\n};\n",
      "type": "registry:component",
      "target": "@ui/form-react-hook.stories.tsx"
    },
    {
      "path": "registry/ui/form-story/form-tanstack-base.stories.tsx",
      "content": "import type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\nimport { useForm as useTanstackForm } from \"@tanstack/react-form\";\nimport type { ComponentProps } from \"react\";\nimport { action } from \"storybook/actions\";\nimport { expect, userEvent } from \"storybook/test\";\nimport * as z from \"zod\";\n\nimport { Button } from \"@/components/ui/button\";\nimport {\n  Field,\n  FieldDescription,\n  FieldError,\n  FieldGroup,\n  FieldLabel,\n} from \"@/components/ui/field\";\nimport { Input } from \"@/components/ui/input\";\n\nconst formSchema = z.object({\n  username: z.string().min(6, {\n    message: \"Username must be at least 6 characters.\",\n  }),\n});\n\ntype ProfileFormValues = z.infer<typeof formSchema>;\n\nfunction TanStackFormProfileForm(props: ComponentProps<\"form\">) {\n  const form = useTanstackForm({\n    defaultValues: {\n      username: \"\",\n    },\n    validators: {\n      onSubmit: formSchema,\n    },\n    onSubmit: async ({ value }) => {\n      action(\"onSubmit\")(value as ProfileFormValues);\n    },\n  });\n\n  return (\n    <form\n      onSubmit={(event) => {\n        event.preventDefault();\n        form.handleSubmit();\n      }}\n      className=\"space-y-8\"\n      {...props}\n    >\n      <FieldGroup>\n        <form.Field name=\"username\">\n          {(field) => {\n            const isInvalid =\n              field.state.meta.isTouched && !field.state.meta.isValid;\n            return (\n              <Field data-invalid={isInvalid}>\n                <FieldLabel htmlFor=\"form-tanstack-username\">\n                  Username\n                </FieldLabel>\n                <Input\n                  id=\"form-tanstack-username\"\n                  name={field.name}\n                  placeholder=\"username\"\n                  value={field.state.value}\n                  onBlur={field.handleBlur}\n                  onChange={(event) => field.handleChange(event.target.value)}\n                  aria-invalid={isInvalid}\n                />\n                <FieldDescription>\n                  This is your public display name.\n                </FieldDescription>\n                {isInvalid && (\n                  <FieldError\n                    errors={field.state.meta.errors.map((error) =>\n                      typeof error === \"string\" ? { message: error } : error,\n                    )}\n                  />\n                )}\n              </Field>\n            );\n          }}\n        </form.Field>\n      </FieldGroup>\n      <Button type=\"submit\">Submit</Button>\n    </form>\n  );\n}\n\n/**\n * Building forms with TanStack Form and Zod.\n */\nconst meta: Meta<typeof TanStackFormProfileForm> = {\n  title: \"ui/base/Form (TanStack Form)\",\n  component: TanStackFormProfileForm,\n  tags: [\"autodocs\"],\n  argTypes: {},\n} satisfies Meta<typeof TanStackFormProfileForm>;\n\nexport default meta;\n\ntype Story = StoryObj<typeof meta>;\n\n/**\n * The default form of the form.\n */\nexport const Default: Story = {};\n\nexport const ShouldSucceedWhenValidInput: Story = {\n  name: \"when typing a valid username, should not show an error message\",\n  tags: [\"!dev\", \"!autodocs\"],\n  play: async ({ canvas, step }) => {\n    await step(\"Type a valid username\", async () => {\n      await userEvent.type(\n        await canvas.findByRole(\"textbox\", { name: /username/i }),\n        \"mockuser\",\n      );\n    });\n\n    await step(\"Click the submit button\", async () => {\n      await userEvent.click(\n        await canvas.findByRole(\"button\", { name: /submit/i }),\n      );\n      expect(\n        await canvas.queryByText(/username must be at least 6 characters/i, {\n          exact: true,\n        }),\n      ).toBeNull();\n    });\n  },\n};\n\nexport const ShouldShowErrorWhenInvalidInput: Story = {\n  name: \"when typing a short username, should show an error message\",\n  tags: [\"!dev\", \"!autodocs\"],\n  play: async ({ canvas, step }) => {\n    await step(\"Type a short username\", async () => {\n      await userEvent.type(\n        await canvas.findByRole(\"textbox\", { name: /username/i }),\n        \"fail\",\n      );\n    });\n\n    await step(\"Click the submit button\", async () => {\n      await userEvent.click(\n        await canvas.findByRole(\"button\", { name: /submit/i }),\n      );\n      expect(\n        await canvas.queryByText(/username must be at least 6 characters/i, {\n          exact: true,\n        }),\n      ).toBeVisible();\n    });\n  },\n};\n",
      "type": "registry:component",
      "target": "@ui/form-tanstack.stories.tsx"
    },
    {
      "path": "registry/ui/hover-card-story/hover-card-base.stories.tsx",
      "content": "import { expect, userEvent, waitFor, within } from \"storybook/test\";\n// Replace nextjs-vite with the name of your framework\nimport type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\n\nimport {\n  HoverCard,\n  HoverCardContent,\n  HoverCardTrigger,\n} from \"@/components/ui/hover-card\";\n\n/**\n * For sighted users to preview content available behind a link.\n */\nconst meta = {\n  title: \"ui/base/HoverCard\",\n  component: HoverCard,\n  tags: [\"autodocs\"],\n  argTypes: {},\n  args: {},\n  render: (args) => (\n    <HoverCard {...args}>\n      <HoverCardTrigger>Hover</HoverCardTrigger>\n      <HoverCardContent>\n        The React Framework - created and maintained by @vercel.\n      </HoverCardContent>\n    </HoverCard>\n  ),\n  parameters: {\n    layout: \"centered\",\n  },\n} satisfies Meta<typeof HoverCard>;\n\nexport default meta;\n\ntype Story = StoryObj<typeof meta>;\n\n/**\n * The default form of the hover card.\n */\nexport const Default: Story = {};\n\n/**\n * Use the `delay` and `closeDelay` props to control the delay before the\n * hover card opens and closes.\n */\nexport const Instant: Story = {\n  render: (args) => (\n    <HoverCard {...args}>\n      <HoverCardTrigger delay={0} closeDelay={0}>\n        Hover\n      </HoverCardTrigger>\n      <HoverCardContent>\n        The React Framework - created and maintained by @vercel.\n      </HoverCardContent>\n    </HoverCard>\n  ),\n};\n\nexport const ShouldShowOnHover: Story = {\n  name: \"when hovering over trigger, should show hover card content\",\n  tags: [\"!dev\", \"!autodocs\"],\n  play: async ({ canvasElement, step }) => {\n    const canvasBody = within(canvasElement.ownerDocument.body);\n\n    await step(\"Hover over the trigger element\", async () => {\n      await userEvent.hover(await canvasBody.findByText(/hover/i));\n      await waitFor(() =>\n        expect(\n          canvasElement.ownerDocument.body.querySelector(\n            \"[data-slot=hover-card-content]\",\n          ),\n        ).toBeVisible(),\n      );\n    });\n    await step(\"Unhover the trigger element\", async () => {\n      await userEvent.unhover(await canvasBody.findByText(/hover/i));\n      await waitFor(() =>\n        expect(\n          canvasElement.ownerDocument.body.querySelector(\n            \"[data-slot=hover-card-content]\",\n          ),\n        ).toBeNull(),\n      );\n    });\n  },\n};\n",
      "type": "registry:component",
      "target": "@ui/hover-card.stories.tsx"
    },
    {
      "path": "registry/ui/input-story/input-base.stories.tsx",
      "content": "import { expect, userEvent } from \"storybook/test\";\n// Replace nextjs-vite with the name of your framework\nimport type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\n\nimport { Button } from \"@/components/ui/button\";\nimport { Input } from \"@/components/ui/input\";\nimport { Label } from \"@/components/ui/label\";\n\n/**\n * Displays a form input field or a component that looks like an input field.\n */\nconst meta = {\n  title: \"ui/base/Input\",\n  component: Input,\n  tags: [\"autodocs\"],\n  argTypes: {},\n  args: {\n    className: \"w-96\",\n    type: \"email\",\n    placeholder: \"Email\",\n    disabled: false,\n  },\n  parameters: {\n    layout: \"centered\",\n  },\n} satisfies Meta<typeof Input>;\n\nexport default meta;\n\ntype Story = StoryObj<typeof meta>;\n\n/**\n * The default form of the input field.\n */\nexport const Default: Story = {};\n\n/**\n * Use the `disabled` prop to make the input non-interactive and appears faded,\n * indicating that input is not currently accepted.\n */\nexport const Disabled: Story = {\n  args: { disabled: true },\n};\n\n/**\n * Use the `Label` component to includes a clear, descriptive label above or\n * alongside the input area to guide users.\n */\nexport const WithLabel: Story = {\n  render: (args) => (\n    <div className=\"grid items-center gap-1.5\">\n      <Label htmlFor=\"email\">{args.placeholder}</Label>\n      <Input {...args} id=\"email\" />\n    </div>\n  ),\n};\n\n/**\n * Use a text element below the input field to provide additional instructions\n * or information to users.\n */\nexport const WithHelperText: Story = {\n  render: (args) => (\n    <div className=\"grid items-center gap-1.5\">\n      <Label htmlFor=\"email-2\">{args.placeholder}</Label>\n      <Input {...args} id=\"email-2\" />\n      <p className=\"text-foreground/60 text-sm\">Enter your email address.</p>\n    </div>\n  ),\n};\n\n/**\n * Use the `Button` component to indicate that the input field can be submitted\n * or used to trigger an action.\n */\nexport const WithButton: Story = {\n  render: (args) => (\n    <div className=\"flex items-center space-x-2\">\n      <Input {...args} />\n      <Button type=\"submit\">Subscribe</Button>\n    </div>\n  ),\n};\n\nexport const ShouldEnterText: Story = {\n  name: \"when user enters text, should see it in the input field\",\n  tags: [\"!dev\", \"!autodocs\"],\n  play: async ({ canvas, step }) => {\n    const input = await canvas.findByPlaceholderText(/email/i);\n    const mockedInput = \"mocked@shadcn.com\";\n\n    await step(\"focus and type into the input field\", async () => {\n      await userEvent.click(input);\n      await userEvent.type(input, mockedInput);\n    });\n\n    expect(input).toHaveValue(mockedInput);\n  },\n};\n",
      "type": "registry:component",
      "target": "@ui/input.stories.tsx"
    },
    {
      "path": "registry/ui/input-group-story/input-group-base.stories.tsx",
      "content": "// Replace nextjs-vite with the name of your framework\nimport type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\nimport {\n  Check,\n  CheckIcon,\n  ChevronDownIcon,\n  Code,\n  Copy,\n  CornerDownLeft,\n  CreditCardIcon,\n  HelpCircle,\n  Info,\n  InfoIcon,\n  Link2Icon,\n  LoaderIcon,\n  MailIcon,\n  MoreHorizontal,\n  RefreshCcw,\n  SearchIcon,\n  Star,\n  StarIcon,\n} from \"lucide-react\";\nimport * as React from \"react\";\n\nimport {\n  ButtonGroup,\n  ButtonGroupText,\n} from \"@/components/ui/button-group\";\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuItem,\n  DropdownMenuTrigger,\n} from \"@/components/ui/dropdown-menu\";\nimport {\n  InputGroup,\n  InputGroupAddon,\n  InputGroupButton,\n  InputGroupInput,\n  InputGroupText,\n  InputGroupTextarea,\n} from \"@/components/ui/input-group\";\nimport { Label } from \"@/components/ui/label\";\nimport {\n  Popover,\n  PopoverContent,\n  PopoverTrigger,\n} from \"@/components/ui/popover\";\nimport { Spinner } from \"@/components/ui/spinner\";\nimport {\n  Tooltip,\n  TooltipContent,\n  TooltipProvider,\n  TooltipTrigger,\n} from \"@/components/ui/tooltip\";\n\n/**\n * Display additional information or actions to an input or textarea.\n */\nconst meta: Meta<typeof InputGroup> = {\n  title: \"ui/base/InputGroup\",\n  component: InputGroup,\n  tags: [\"autodocs\"],\n  parameters: {\n    layout: \"centered\",\n  },\n} satisfies Meta<typeof InputGroup>;\n\nexport default meta;\n\ntype Story = StoryObj<typeof meta>;\n\n/**\n * Input groups with icon addons for visual enhancement.\n */\nexport const WithIcons: Story = {\n  render: (args) => (\n    <div className=\"grid w-full max-w-sm gap-6\">\n      <InputGroup {...args}>\n        <InputGroupInput placeholder=\"Search...\" />\n        <InputGroupAddon>\n          <SearchIcon />\n        </InputGroupAddon>\n      </InputGroup>\n      <InputGroup {...args}>\n        <InputGroupInput type=\"email\" placeholder=\"Enter your email\" />\n        <InputGroupAddon>\n          <MailIcon />\n        </InputGroupAddon>\n      </InputGroup>\n      <InputGroup {...args}>\n        <InputGroupInput placeholder=\"Card number\" />\n        <InputGroupAddon>\n          <CreditCardIcon />\n        </InputGroupAddon>\n        <InputGroupAddon align=\"inline-end\">\n          <CheckIcon />\n        </InputGroupAddon>\n      </InputGroup>\n      <InputGroup {...args}>\n        <InputGroupInput placeholder=\"Card number\" />\n        <InputGroupAddon align=\"inline-end\">\n          <StarIcon />\n          <InfoIcon />\n        </InputGroupAddon>\n      </InputGroup>\n    </div>\n  ),\n};\n\n/**\n * Display additional text information alongside inputs.\n */\nexport const WithText: Story = {\n  render: (args) => (\n    <div className=\"grid w-full max-w-sm gap-6\">\n      <InputGroup {...args}>\n        <InputGroupAddon>\n          <InputGroupText>$</InputGroupText>\n        </InputGroupAddon>\n        <InputGroupInput placeholder=\"0.00\" />\n        <InputGroupAddon align=\"inline-end\">\n          <InputGroupText>USD</InputGroupText>\n        </InputGroupAddon>\n      </InputGroup>\n      <InputGroup {...args}>\n        <InputGroupAddon>\n          <InputGroupText>https://</InputGroupText>\n        </InputGroupAddon>\n        <InputGroupInput placeholder=\"example.com\" className=\"!pl-0.5\" />\n        <InputGroupAddon align=\"inline-end\">\n          <InputGroupText>.com</InputGroupText>\n        </InputGroupAddon>\n      </InputGroup>\n      <InputGroup {...args}>\n        <InputGroupInput placeholder=\"Enter your username\" />\n        <InputGroupAddon align=\"inline-end\">\n          <InputGroupText>@company.com</InputGroupText>\n        </InputGroupAddon>\n      </InputGroup>\n      <InputGroup {...args}>\n        <InputGroupTextarea placeholder=\"Enter your message\" />\n        <InputGroupAddon align=\"block-end\">\n          <InputGroupText className=\"text-muted-foreground text-xs\">\n            120 characters left\n          </InputGroupText>\n        </InputGroupAddon>\n      </InputGroup>\n    </div>\n  ),\n};\n\n/**\n * Add buttons to perform actions within the input group.\n */\nexport const WithButtons: Story = {\n  render: (args) => {\n    const [isCopied, setIsCopied] = React.useState(false);\n    const [isFavorite, setIsFavorite] = React.useState(false);\n\n    const copyToClipboard = (text: string) => {\n      navigator.clipboard.writeText(text);\n      setIsCopied(true);\n      setTimeout(() => setIsCopied(false), 2000);\n    };\n\n    return (\n      <div className=\"grid w-full max-w-sm gap-6\">\n        <InputGroup {...args}>\n          <InputGroupInput placeholder=\"https://x.com/shadcn\" readOnly />\n          <InputGroupAddon align=\"inline-end\">\n            <InputGroupButton\n              aria-label=\"Copy\"\n              title=\"Copy\"\n              size=\"icon-xs\"\n              nativeButton\n              onClick={() => {\n                copyToClipboard(\"https://x.com/shadcn\");\n              }}\n            >\n              {isCopied ? <Check /> : <Copy />}\n            </InputGroupButton>\n          </InputGroupAddon>\n        </InputGroup>\n        <InputGroup className=\"[--radius:9999px]\">\n          <Popover>\n            <InputGroupAddon>\n              <PopoverTrigger\n                nativeButton\n                render={\n                  <InputGroupButton\n                    variant=\"secondary\"\n                    size=\"icon-xs\"\n                    nativeButton\n                  />\n                }\n              >\n                <Info />\n              </PopoverTrigger>\n            </InputGroupAddon>\n            <PopoverContent\n              align=\"start\"\n              className=\"flex flex-col gap-1 rounded-xl text-sm\"\n            >\n              <p className=\"font-medium\">Your connection is not secure.</p>\n              <p>\n                You should not enter any sensitive information on this site.\n              </p>\n            </PopoverContent>\n          </Popover>\n          <InputGroupAddon className=\"pl-1.5 text-muted-foreground\">\n            https://\n          </InputGroupAddon>\n          <InputGroupInput id=\"input-secure-19\" />\n          <InputGroupAddon align=\"inline-end\">\n            <InputGroupButton\n              onClick={() => setIsFavorite(!isFavorite)}\n              size=\"icon-xs\"\n              nativeButton\n            >\n              <Star\n                data-favorite={isFavorite}\n                className=\"data-[favorite=true]:fill-blue-600 data-[favorite=true]:stroke-blue-600\"\n              />\n            </InputGroupButton>\n          </InputGroupAddon>\n        </InputGroup>\n        <InputGroup {...args}>\n          <InputGroupInput placeholder=\"Type to search...\" />\n          <InputGroupAddon align=\"inline-end\">\n            <InputGroupButton variant=\"secondary\" nativeButton>\n              Search\n            </InputGroupButton>\n          </InputGroupAddon>\n        </InputGroup>\n      </div>\n    );\n  },\n};\n\n/**\n * Add tooltips to provide additional context or help.\n */\nexport const WithTooltips: Story = {\n  render: (args) => (\n    <div className=\"grid w-full max-w-sm gap-4\">\n      <TooltipProvider>\n        <InputGroup {...args}>\n          <InputGroupInput placeholder=\"Enter password\" type=\"password\" />\n          <InputGroupAddon align=\"inline-end\">\n            <Tooltip>\n              <InputGroupButton\n                render={<TooltipTrigger />}\n                variant=\"ghost\"\n                aria-label=\"Info\"\n                size=\"icon-xs\"\n              >\n                <InfoIcon />\n              </InputGroupButton>\n              <TooltipContent>\n                <p>Password must be at least 8 characters</p>\n              </TooltipContent>\n            </Tooltip>\n          </InputGroupAddon>\n        </InputGroup>\n        <InputGroup {...args}>\n          <InputGroupInput placeholder=\"Your email address\" />\n          <InputGroupAddon align=\"inline-end\">\n            <Tooltip>\n              <InputGroupButton\n                render={<TooltipTrigger />}\n                variant=\"ghost\"\n                aria-label=\"Help\"\n                size=\"icon-xs\"\n              >\n                <HelpCircle />\n              </InputGroupButton>\n              <TooltipContent>\n                <p>We'll use this to send you notifications</p>\n              </TooltipContent>\n            </Tooltip>\n          </InputGroupAddon>\n        </InputGroup>\n        <InputGroup {...args}>\n          <InputGroupInput placeholder=\"Enter API key\" />\n          <Tooltip>\n            <TooltipTrigger render={<InputGroupAddon />}>\n              <InputGroupButton\n                variant=\"ghost\"\n                aria-label=\"Help\"\n                size=\"icon-xs\"\n              >\n                <HelpCircle />\n              </InputGroupButton>\n            </TooltipTrigger>\n            <TooltipContent side=\"left\">\n              <p>Click for help with API keys</p>\n            </TooltipContent>\n          </Tooltip>\n        </InputGroup>\n      </TooltipProvider>\n    </div>\n  ),\n};\n\n/**\n * Input groups also work with textarea components.\n */\nexport const WithTextarea: Story = {\n  render: (args) => {\n    return (\n      <div className=\"grid w-full max-w-md gap-4\">\n        <InputGroup {...args}>\n          <InputGroupTextarea\n            id=\"textarea-code-32\"\n            placeholder=\"console.log('Hello, world!');\"\n            className=\"min-h-[200px]\"\n          />\n          <InputGroupAddon align=\"block-end\" className=\"border-t\">\n            <InputGroupText>Line 1, Column 1</InputGroupText>\n            <InputGroupButton size=\"sm\" className=\"ml-auto\" variant=\"default\">\n              Run <CornerDownLeft />\n            </InputGroupButton>\n          </InputGroupAddon>\n          <InputGroupAddon align=\"block-start\" className=\"border-b\">\n            <InputGroupText className=\"font-medium font-mono\">\n              <Code />\n              script.js\n            </InputGroupText>\n            <InputGroupButton className=\"ml-auto\" size=\"icon-xs\">\n              <RefreshCcw />\n            </InputGroupButton>\n            <InputGroupButton variant=\"ghost\" size=\"icon-xs\">\n              <Copy />\n            </InputGroupButton>\n          </InputGroupAddon>\n        </InputGroup>\n      </div>\n    );\n  },\n};\n\n/**\n * Show loading indicators while processing input.\n */\nexport const WithSpinner: Story = {\n  render: (args) => (\n    <div className=\"grid w-full max-w-sm gap-4\">\n      <InputGroup {...args} data-disabled>\n        <InputGroupInput placeholder=\"Searching...\" disabled />\n        <InputGroupAddon align=\"inline-end\">\n          <Spinner />\n        </InputGroupAddon>\n      </InputGroup>\n      <InputGroup data-disabled>\n        <InputGroupInput placeholder=\"Processing...\" disabled />\n        <InputGroupAddon>\n          <Spinner />\n        </InputGroupAddon>\n      </InputGroup>\n      <InputGroup data-disabled>\n        <InputGroupInput placeholder=\"Saving changes...\" disabled />\n        <InputGroupAddon align=\"inline-end\">\n          <InputGroupText>Saving...</InputGroupText>\n          <Spinner />\n        </InputGroupAddon>\n      </InputGroup>\n      <InputGroup data-disabled>\n        <InputGroupInput placeholder=\"Refreshing data...\" disabled />\n        <InputGroupAddon>\n          <LoaderIcon className=\"animate-spin\" />\n        </InputGroupAddon>\n        <InputGroupAddon align=\"inline-end\">\n          <InputGroupText className=\"text-muted-foreground\">\n            Please wait...\n          </InputGroupText>\n        </InputGroupAddon>\n      </InputGroup>\n    </div>\n  ),\n};\n\n/**\n * Add labels within input groups to improve accessibility.\n */\nexport const WithLabels: Story = {\n  render: (args) => (\n    <div className=\"grid w-full max-w-sm gap-4\">\n      <TooltipProvider>\n        <InputGroup {...args}>\n          <InputGroupInput id=\"email\" placeholder=\"shadcn\" />\n          <InputGroupAddon>\n            <Label htmlFor=\"email\">@</Label>\n          </InputGroupAddon>\n        </InputGroup>\n        <InputGroup {...args}>\n          <InputGroupInput id=\"email-2\" placeholder=\"shadcn@vercel.com\" />\n          <InputGroupAddon align=\"block-start\">\n            <Label htmlFor=\"email-2\" className=\"text-foreground\">\n              Email\n            </Label>\n            <Tooltip>\n              <InputGroupButton\n                render={<TooltipTrigger />}\n                variant=\"ghost\"\n                aria-label=\"Help\"\n                className=\"ml-auto rounded-full\"\n                size=\"icon-xs\"\n              >\n                <InfoIcon />\n              </InputGroupButton>\n              <TooltipContent>\n                <p>We'll use this to send you notifications</p>\n              </TooltipContent>\n            </Tooltip>\n          </InputGroupAddon>\n        </InputGroup>\n      </TooltipProvider>\n    </div>\n  ),\n};\n\n/**\n * Pair input groups with dropdown menus for complex interactions.\n */\nexport const WithDropdowns: Story = {\n  render: (args) => (\n    <div className=\"grid w-full max-w-sm gap-4\">\n      <InputGroup {...args}>\n        <InputGroupInput placeholder=\"Enter file name\" />\n        <InputGroupAddon align=\"inline-end\">\n          <DropdownMenu>\n            <DropdownMenuTrigger\n              render={\n                <InputGroupButton\n                  variant=\"ghost\"\n                  aria-label=\"More\"\n                  size=\"icon-xs\"\n                />\n              }\n            >\n              <MoreHorizontal />\n            </DropdownMenuTrigger>\n            <DropdownMenuContent align=\"end\">\n              <DropdownMenuItem>Settings</DropdownMenuItem>\n              <DropdownMenuItem>Copy path</DropdownMenuItem>\n              <DropdownMenuItem>Open location</DropdownMenuItem>\n            </DropdownMenuContent>\n          </DropdownMenu>\n        </InputGroupAddon>\n      </InputGroup>\n      <InputGroup className=\"[--radius:1rem]\">\n        <InputGroupInput placeholder=\"Enter search query\" />\n        <InputGroupAddon align=\"inline-end\">\n          <DropdownMenu>\n            <DropdownMenuTrigger\n              render={\n                <InputGroupButton variant=\"ghost\" className=\"!pr-1.5 text-xs\" />\n              }\n            >\n              Search In... <ChevronDownIcon className=\"size-3\" />\n            </DropdownMenuTrigger>\n            <DropdownMenuContent align=\"end\" className=\"[--radius:0.95rem]\">\n              <DropdownMenuItem>Documentation</DropdownMenuItem>\n              <DropdownMenuItem>Blog Posts</DropdownMenuItem>\n              <DropdownMenuItem>Changelog</DropdownMenuItem>\n            </DropdownMenuContent>\n          </DropdownMenu>\n        </InputGroupAddon>\n      </InputGroup>\n    </div>\n  ),\n};\n\n/**\n * Wrap input groups with button groups to create prefixes and suffixes.\n */\nexport const WithButtonGroup: Story = {\n  render: (args) => (\n    <div className=\"grid w-full max-w-sm gap-6\">\n      <ButtonGroup>\n        <ButtonGroupText render={<Label htmlFor=\"url\" />}>\n          https://\n        </ButtonGroupText>\n        <InputGroup {...args}>\n          <InputGroupInput id=\"url\" />\n          <InputGroupAddon align=\"inline-end\">\n            <Link2Icon />\n          </InputGroupAddon>\n        </InputGroup>\n        <ButtonGroupText>.com</ButtonGroupText>\n      </ButtonGroup>\n    </div>\n  ),\n};\n",
      "type": "registry:component",
      "target": "@ui/input-group.stories.tsx"
    },
    {
      "path": "registry/ui/input-otp-story/input-otp-base.stories.tsx",
      "content": "import { expect, fn, userEvent } from \"storybook/test\";\n// Replace nextjs-vite with the name of your framework\nimport type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\nimport { REGEXP_ONLY_DIGITS, REGEXP_ONLY_DIGITS_AND_CHARS } from \"input-otp\";\n\nimport {\n  InputOTP,\n  InputOTPGroup,\n  InputOTPSeparator,\n  InputOTPSlot,\n} from \"@/components/ui/input-otp\";\n\n/**\n * Accessible one-time password component with copy paste functionality.\n */\nconst meta = {\n  title: \"ui/base/InputOTP\",\n  component: InputOTP,\n  tags: [\"autodocs\"],\n  argTypes: {},\n  args: {\n    maxLength: 6,\n    onChange: fn(),\n    onComplete: fn(),\n    pattern: REGEXP_ONLY_DIGITS_AND_CHARS,\n    children: null,\n    \"aria-label\": \"One-time password\",\n  },\n\n  render: (args) => (\n    <InputOTP {...args} render={undefined}>\n      <InputOTPGroup>\n        <InputOTPSlot index={0} />\n        <InputOTPSlot index={1} />\n        <InputOTPSlot index={2} />\n        <InputOTPSlot index={3} />\n        <InputOTPSlot index={4} />\n        <InputOTPSlot index={5} />\n      </InputOTPGroup>\n    </InputOTP>\n  ),\n  parameters: {\n    layout: \"centered\",\n  },\n} satisfies Meta<typeof InputOTP>;\n\nexport default meta;\n\ntype Story = StoryObj<typeof meta>;\n\n/**\n * The default form of the InputOTP field.\n */\nexport const Default: Story = {};\n\n/**\n * The number form of the InputOTP field.\n */\nexport const OnlyNumbers: Story = {\n  args: {\n    pattern: REGEXP_ONLY_DIGITS,\n  },\n};\n\n/**\n * Use multiple groups to separate the input slots.\n */\nexport const SeparatedGroup: Story = {\n  render: (args) => (\n    <InputOTP {...args} render={undefined}>\n      <InputOTPGroup>\n        <InputOTPSlot index={0} />\n        <InputOTPSlot index={1} />\n        <InputOTPSlot index={2} />\n      </InputOTPGroup>\n      <InputOTPSeparator />\n      <InputOTPGroup>\n        <InputOTPSlot index={3} />\n        <InputOTPSlot index={4} />\n        <InputOTPSlot index={5} />\n      </InputOTPGroup>\n    </InputOTP>\n  ),\n};\n\nexport const ShouldAcceptTextWhenTyping: Story = {\n  name: \"when typing text, should call onChange and onComplete\",\n  tags: [\"!dev\", \"!autodocs\"],\n  play: async ({ args, canvas, step }) => {\n    const inputTextbox = await canvas.findByRole(\"textbox\");\n\n    await step(\"type into input textbox\", async () => {\n      await userEvent.click(inputTextbox);\n      await userEvent.type(inputTextbox, \"mocked\");\n      expect(args.onChange).toHaveBeenCalledTimes(6);\n    });\n\n    await step(\"finish typing by pressing Enter\", async () => {\n      await userEvent.keyboard(\"{enter}\");\n      expect(args.onComplete).toHaveBeenCalledTimes(1);\n    });\n  },\n};\n\nexport const ShouldAcceptOnlyNumbersWhenRestricted: Story = {\n  ...OnlyNumbers,\n  name: \"when only numbers are allowed, should call onChange for numbers and onComplete\",\n  tags: [\"!dev\", \"!autodocs\"],\n  play: async ({ args, canvas, step }) => {\n    const inputTextbox = await canvas.findByRole(\"textbox\");\n\n    await step(\"type text into input textbox\", async () => {\n      await userEvent.click(inputTextbox);\n      await userEvent.type(inputTextbox, \"mocked\");\n      expect(args.onChange).toHaveBeenCalledTimes(0);\n    });\n\n    await step(\"type numbers into input textbox\", async () => {\n      await userEvent.type(inputTextbox, \"123456\");\n      expect(args.onChange).toHaveBeenCalledTimes(6);\n    });\n\n    await step(\"finish typing by pressing Enter\", async () => {\n      await userEvent.keyboard(\"{enter}\");\n      expect(args.onComplete).toHaveBeenCalledTimes(1);\n    });\n  },\n};\n",
      "type": "registry:component",
      "target": "@ui/input-otp.stories.tsx"
    },
    {
      "path": "registry/ui/label-story/label-base.stories.tsx",
      "content": "// Replace nextjs-vite with the name of your framework\nimport type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\n\nimport { Label } from \"@/components/ui/label\";\n\n/**\n * Renders an accessible label associated with controls.\n */\nconst meta = {\n  title: \"ui/base/Label\",\n  component: Label,\n  tags: [\"autodocs\"],\n  argTypes: {\n    children: {\n      control: { type: \"text\" },\n    },\n  },\n  args: {\n    children: \"Your email address\",\n    htmlFor: \"email\",\n  },\n} satisfies Meta<typeof Label>;\n\nexport default meta;\n\ntype Story = StoryObj<typeof Label>;\n\n/**\n * The default form of the label.\n */\nexport const Default: Story = {};\n",
      "type": "registry:component",
      "target": "@ui/label.stories.tsx"
    },
    {
      "path": "registry/ui/marker-story/marker-base.stories.tsx",
      "content": "// Replace nextjs-vite with the name of your framework\nimport type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\nimport {\n  BookOpenCheckIcon,\n  CheckIcon,\n  FileTextIcon,\n  GitBranchIcon,\n  RotateCcwIcon,\n  SearchIcon,\n} from \"lucide-react\";\nimport { expect, userEvent, within } from \"storybook/test\";\n\nimport {\n  Marker,\n  MarkerContent,\n  MarkerIcon,\n} from \"@/components/ui/marker\";\nimport { Spinner } from \"@/components/ui/spinner\";\n\n/**\n * Displays inline conversation status, notes, actions, and separators.\n */\nconst meta: Meta<typeof Marker> = {\n  title: \"ui/base/Marker\",\n  component: Marker,\n  tags: [\"autodocs\"],\n  parameters: {\n    layout: \"centered\",\n  },\n  argTypes: {\n    variant: {\n      control: \"select\",\n      options: [\"default\", \"separator\", \"border\"],\n    },\n  },\n  args: {\n    variant: \"default\",\n  },\n  decorators: (Story) => (\n    <div className=\"w-full min-w-sm max-w-md\">\n      <Story />\n    </div>\n  ),\n} satisfies Meta<typeof Marker>;\n\nexport default meta;\n\ntype Story = StoryObj<typeof meta>;\n\n/**\n * The default marker for inline notes and status updates.\n */\nexport const Default: Story = {\n  render: (args) => (\n    <Marker {...args}>\n      <MarkerContent>Explored 4 files</MarkerContent>\n    </Marker>\n  ),\n};\n\n/**\n * Use the border variant for status rows that separate the next row.\n */\nexport const Border: Story = {\n  args: {\n    variant: \"border\",\n  },\n  render: (args) => (\n    <Marker {...args}>\n      <MarkerContent>Opened implementation notes</MarkerContent>\n    </Marker>\n  ),\n};\n\n/**\n * Use the separator variant for date breaks or labeled transcript sections.\n */\nexport const Separator: Story = {\n  args: {\n    variant: \"separator\",\n  },\n  render: (args) => (\n    <Marker {...args}>\n      <MarkerContent>Today</MarkerContent>\n    </Marker>\n  ),\n};\n\n/**\n * Use `role=\"status\"` for progress markers that should be announced.\n */\nexport const Status: Story = {\n  render: (args) => (\n    <Marker {...args} role=\"status\">\n      <MarkerIcon>\n        <Spinner />\n      </MarkerIcon>\n      <MarkerContent>Compacting conversation</MarkerContent>\n    </Marker>\n  ),\n};\n\n/**\n * Add shimmer to streaming or in-progress marker text.\n */\nexport const Shimmer: Story = {\n  render: (args) => (\n    <Marker {...args}>\n      <MarkerContent className=\"shimmer\">Thinking...</MarkerContent>\n    </Marker>\n  ),\n};\n\n/**\n * Add an icon to make a system note easier to scan.\n */\nexport const WithIcon: Story = {\n  render: (args) => (\n    <Marker {...args}>\n      <MarkerIcon>\n        <GitBranchIcon />\n      </MarkerIcon>\n      <MarkerContent>Switched to a new branch</MarkerContent>\n    </Marker>\n  ),\n};\n\n/**\n * Render a marker as a button for inline transcript actions.\n */\nexport const ButtonMarker: Story = {\n  render: (args) => (\n    <Marker {...args} render={<button type=\"button\" />}>\n      <MarkerIcon>\n        <RotateCcwIcon />\n      </MarkerIcon>\n      <MarkerContent>Revert this change</MarkerContent>\n    </Marker>\n  ),\n};\n\n/**\n * Render a marker as a link when the note navigates to related context.\n */\nexport const LinkMarker: Story = {\n  render: (args) => (\n    <Marker {...args} render={<a href=\"#pull-request\" />}>\n      <MarkerContent>View the pull request</MarkerContent>\n    </Marker>\n  ),\n};\n\n/**\n * Compose a short timeline with separators, bordered rows, and icon markers.\n */\nexport const Timeline: Story = {\n  render: () => (\n    <div className=\"flex flex-col gap-4\">\n      <Marker variant=\"separator\">\n        <MarkerContent>Today</MarkerContent>\n      </Marker>\n      <Marker variant=\"border\">\n        <MarkerIcon>\n          <SearchIcon />\n        </MarkerIcon>\n        <MarkerContent>Reviewed 8 related files</MarkerContent>\n      </Marker>\n      <Marker>\n        <MarkerIcon>\n          <BookOpenCheckIcon />\n        </MarkerIcon>\n        <MarkerContent>Implementation notes are up to date</MarkerContent>\n      </Marker>\n    </div>\n  ),\n};\n\n/**\n * Verify interactive markers expose a button and respond to clicks.\n */\nexport const ButtonInteraction: Story = {\n  tags: [\"!dev\", \"!autodocs\"],\n  render: () => (\n    <Marker render={<button type=\"button\" />}>\n      <MarkerIcon>\n        <CheckIcon />\n      </MarkerIcon>\n      <MarkerContent>Mark as resolved</MarkerContent>\n    </Marker>\n  ),\n  play: async ({ canvasElement }) => {\n    const canvas = within(canvasElement);\n    const button = canvas.getByRole(\"button\", { name: /mark as resolved/i });\n\n    await userEvent.click(button);\n    expect(button).toHaveFocus();\n  },\n};\n\n/**\n * Verify status markers keep progress text available to assistive tech.\n */\nexport const StatusAnnounced: Story = {\n  tags: [\"!dev\", \"!autodocs\"],\n  render: () => (\n    <Marker role=\"status\">\n      <MarkerIcon>\n        <FileTextIcon />\n      </MarkerIcon>\n      <MarkerContent>Reading project files</MarkerContent>\n    </Marker>\n  ),\n  play: async ({ canvasElement }) => {\n    const canvas = within(canvasElement);\n\n    expect(canvas.getByRole(\"status\")).toHaveTextContent(\n      \"Reading project files\",\n    );\n  },\n};\n",
      "type": "registry:component",
      "target": "@ui/marker.stories.tsx"
    },
    {
      "path": "registry/ui/menubar-story/menubar-base.stories.tsx",
      "content": "// Replace nextjs-vite with the name of your framework\nimport type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\n\nimport {\n  Menubar,\n  MenubarCheckboxItem,\n  MenubarContent,\n  MenubarGroup,\n  MenubarItem,\n  MenubarLabel,\n  MenubarMenu,\n  MenubarRadioGroup,\n  MenubarRadioItem,\n  MenubarSeparator,\n  MenubarShortcut,\n  MenubarSub,\n  MenubarSubContent,\n  MenubarSubTrigger,\n  MenubarTrigger,\n} from \"@/components/ui/menubar\";\nimport { expect, userEvent, within } from \"storybook/test\";\n\n/**\n * A visually persistent menu common in desktop applications that provides\n * quick access to a consistent set of commands.\n */\nconst meta = {\n  title: \"ui/base/Menubar\",\n  component: Menubar,\n  tags: [\"autodocs\"],\n  argTypes: {},\n\n  render: (args) => (\n    <Menubar {...args}>\n      <MenubarMenu>\n        <MenubarTrigger>File</MenubarTrigger>\n        <MenubarContent>\n          <MenubarItem>\n            New Tab <MenubarShortcut>⌘T</MenubarShortcut>\n          </MenubarItem>\n          <MenubarItem>New Window</MenubarItem>\n          <MenubarSeparator />\n          <MenubarItem disabled>Share</MenubarItem>\n          <MenubarSeparator />\n          <MenubarItem>Print</MenubarItem>\n        </MenubarContent>\n      </MenubarMenu>\n    </Menubar>\n  ),\n  parameters: {\n    layout: \"centered\",\n  },\n} satisfies Meta<typeof Menubar>;\n\nexport default meta;\n\ntype Story = StoryObj<typeof meta>;\n\n/**\n * The default form of the menubar.\n */\nexport const Default: Story = {};\n\n/**\n * A menubar with a submenu.\n */\nexport const WithSubmenu: Story = {\n  render: (args) => (\n    <Menubar {...args}>\n      <MenubarMenu>\n        <MenubarTrigger>Actions</MenubarTrigger>\n        <MenubarContent>\n          <MenubarItem>Download</MenubarItem>\n          <MenubarSub>\n            <MenubarSubTrigger>Share</MenubarSubTrigger>\n            <MenubarSubContent>\n              <MenubarItem>Email link</MenubarItem>\n              <MenubarItem>Messages</MenubarItem>\n              <MenubarItem>Notes</MenubarItem>\n            </MenubarSubContent>\n          </MenubarSub>\n        </MenubarContent>\n      </MenubarMenu>\n    </Menubar>\n  ),\n};\n\n/**\n * A menubar with radio items.\n */\nexport const WithRadioItems: Story = {\n  render: (args) => (\n    <Menubar {...args}>\n      <MenubarMenu>\n        <MenubarTrigger>View</MenubarTrigger>\n        <MenubarContent>\n          <MenubarLabel inset>Device Size</MenubarLabel>\n          <MenubarRadioGroup value=\"md\">\n            <MenubarRadioItem value=\"sm\">Small</MenubarRadioItem>\n            <MenubarRadioItem value=\"md\">Medium</MenubarRadioItem>\n            <MenubarRadioItem value=\"lg\">Large</MenubarRadioItem>\n          </MenubarRadioGroup>\n        </MenubarContent>\n      </MenubarMenu>\n    </Menubar>\n  ),\n};\n\n/**\n * A menubar with checkbox items.\n */\nexport const WithCheckboxItems: Story = {\n  render: (args) => (\n    <Menubar {...args}>\n      <MenubarMenu>\n        <MenubarTrigger>Filters</MenubarTrigger>\n        <MenubarContent>\n          <MenubarItem>Show All</MenubarItem>\n          <MenubarGroup>\n            <MenubarCheckboxItem checked>Unread</MenubarCheckboxItem>\n            <MenubarCheckboxItem checked>Important</MenubarCheckboxItem>\n            <MenubarCheckboxItem>Flagged</MenubarCheckboxItem>\n          </MenubarGroup>\n        </MenubarContent>\n      </MenubarMenu>\n    </Menubar>\n  ),\n};\n\nexport const ShouldOpenClose: Story = {\n  name: \"when clicking an item, should close the menubar\",\n  tags: [\"!dev\", \"!autodocs\"],\n  play: async ({ canvasElement, step }) => {\n    const canvasBody = within(canvasElement.ownerDocument.body);\n\n    await step(\"open the menubar\", async () => {\n      await userEvent.click(\n        await canvasBody.findByRole(\"menuitem\", { name: /file/i }),\n      );\n      expect(await canvasBody.findByRole(\"menu\")).toBeInTheDocument();\n    });\n\n    const items = await canvasBody.findAllByRole(\"menuitem\");\n    expect(items).toHaveLength(5);\n\n    await step(\"click the first item to close the menubar\", async () => {\n      await userEvent.click(items[0], { delay: 100 });\n    });\n  },\n};\n",
      "type": "registry:component",
      "target": "@ui/menubar.stories.tsx"
    },
    {
      "path": "registry/ui/message-story/message-base.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/base/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"
    },
    {
      "path": "registry/ui/message-scroller-story/message-scroller-base.stories.tsx",
      "content": "// Replace nextjs-vite with the name of your framework\nimport type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\nimport * as React from \"react\";\nimport { expect, userEvent, within } from \"storybook/test\";\n\nimport { Bubble, BubbleContent } from \"@/components/ui/bubble\";\nimport { Button } from \"@/components/ui/button\";\nimport { Marker, MarkerContent } from \"@/components/ui/marker\";\nimport { Message, MessageContent } from \"@/components/ui/message\";\nimport {\n  MessageScroller,\n  MessageScrollerButton,\n  MessageScrollerContent,\n  MessageScrollerItem,\n  MessageScrollerProvider,\n  MessageScrollerViewport,\n  useMessageScroller,\n  useMessageScrollerScrollable,\n} from \"@/components/ui/message-scroller\";\n\n/**\n * Provides chat transcript scrolling with anchoring, jump controls, and live-edge behavior.\n */\nconst meta: Meta<typeof MessageScroller> = {\n  title: \"ui/base/MessageScroller\",\n  component: MessageScroller,\n  tags: [\"autodocs\"],\n  parameters: {\n    layout: \"centered\",\n  },\n  decorators: (Story) => (\n    <div className=\"h-96 w-full min-w-sm max-w-lg rounded-md border\">\n      <Story />\n    </div>\n  ),\n} satisfies Meta<typeof MessageScroller>;\n\nexport default meta;\n\ntype Story = StoryObj<typeof meta>;\n\ntype TranscriptMessage = {\n  id: string;\n  role: \"user\" | \"assistant\";\n  content: string;\n};\n\nconst transcript: TranscriptMessage[] = [\n  {\n    id: \"m1\",\n    role: \"user\",\n    content: \"Can you review the activation dip after workspace creation?\",\n  },\n  {\n    id: \"m2\",\n    role: \"assistant\",\n    content:\n      \"The sharpest drop is between creating the workspace and inviting the first teammate.\",\n  },\n  {\n    id: \"m3\",\n    role: \"user\",\n    content: \"What should I compare before we change the onboarding flow?\",\n  },\n  {\n    id: \"m4\",\n    role: \"assistant\",\n    content:\n      \"Compare template users, blank workspace users, and people who skip invites but return within 24 hours.\",\n  },\n  {\n    id: \"m5\",\n    role: \"user\",\n    content: \"Can you turn that into an experiment?\",\n  },\n  {\n    id: \"m6\",\n    role: \"assistant\",\n    content:\n      \"Create a variant that shows a short checklist after workspace creation, then measure first invite completion and 24-hour return rate.\",\n  },\n];\n\nfunction StoryShell({\n  children,\n  controls,\n}: {\n  children: React.ReactNode;\n  controls?: React.ReactNode;\n}) {\n  return (\n    <div className=\"flex size-full min-h-0 flex-col overflow-hidden\">\n      <div className=\"min-h-0 flex-1 overflow-hidden\">{children}</div>\n      {controls ? (\n        <div className=\"shrink-0 border-t p-2\">{controls}</div>\n      ) : null}\n    </div>\n  );\n}\n\nfunction ScrollerSlot({ children }: { children: React.ReactNode }) {\n  return <div className=\"size-full min-h-0 overflow-hidden\">{children}</div>;\n}\n\nfunction RestoreMessagePosition({\n  messageId,\n  onRestored,\n}: {\n  messageId: string | null;\n  onRestored: () => void;\n}) {\n  const { scrollToMessage } = useMessageScroller();\n\n  React.useLayoutEffect(() => {\n    if (!messageId) {\n      return;\n    }\n\n    const frame = window.requestAnimationFrame(() => {\n      scrollToMessage(messageId, { align: \"start\", behavior: \"auto\" });\n      onRestored();\n    });\n\n    return () => window.cancelAnimationFrame(frame);\n  }, [messageId, onRestored, scrollToMessage]);\n\n  return null;\n}\n\nfunction TranscriptMessage({ message }: { message: TranscriptMessage }) {\n  const isUser = message.role === \"user\";\n\n  return (\n    <Message align={isUser ? \"end\" : \"start\"}>\n      <MessageContent>\n        <Bubble variant={isUser ? \"default\" : \"secondary\"}>\n          <BubbleContent>{message.content}</BubbleContent>\n        </Bubble>\n      </MessageContent>\n    </Message>\n  );\n}\n\nfunction Transcript({\n  messages = transcript,\n  anchorRole = \"user\",\n}: {\n  messages?: TranscriptMessage[];\n  anchorRole?: TranscriptMessage[\"role\"];\n}) {\n  return (\n    <MessageScroller>\n      <MessageScrollerViewport>\n        <MessageScrollerContent>\n          {messages.map((message) => (\n            <MessageScrollerItem\n              key={message.id}\n              messageId={message.id}\n              scrollAnchor={message.role === anchorRole}\n            >\n              <TranscriptMessage message={message} />\n            </MessageScrollerItem>\n          ))}\n        </MessageScrollerContent>\n      </MessageScrollerViewport>\n      <MessageScrollerButton />\n    </MessageScroller>\n  );\n}\n\n/**\n * The default transcript scrolls within a height-constrained container.\n */\nexport const Default: Story = {\n  render: () => (\n    <MessageScrollerProvider>\n      <Transcript />\n    </MessageScrollerProvider>\n  ),\n};\n\n/**\n * Anchor new turns to user messages so the prompt starts near the top edge.\n */\nexport const AnchoringTurns: Story = {\n  render: () => {\n    const [messages, setMessages] = React.useState(transcript.slice(0, 2));\n\n    return (\n      <StoryShell\n        controls={\n          <Button\n            size=\"sm\"\n            onClick={() =>\n              setMessages((current) => [\n                ...current,\n                {\n                  id: `user-${current.length}`,\n                  role: \"user\",\n                  content: \"What should we inspect next?\",\n                },\n                {\n                  id: `assistant-${current.length}`,\n                  role: \"assistant\",\n                  content: \"Start with the story coverage and registry output.\",\n                },\n              ])\n            }\n          >\n            Send Message\n          </Button>\n        }\n      >\n        <ScrollerSlot>\n          <MessageScrollerProvider autoScroll>\n            <Transcript messages={messages} />\n          </MessageScrollerProvider>\n        </ScrollerSlot>\n      </StoryShell>\n    );\n  },\n};\n\n/**\n * Auto-scroll follows the live edge while a scripted response streams in.\n */\nexport const Streaming: Story = {\n  render: () => {\n    const [reply, setReply] = React.useState(\"Ready to stream.\");\n\n    return (\n      <StoryShell\n        controls={\n          <Button\n            size=\"sm\"\n            onClick={() =>\n              setReply(\n                \"Streaming is simulated. The reply grows while the scroller keeps the live edge in view.\",\n              )\n            }\n          >\n            Send\n          </Button>\n        }\n      >\n        <ScrollerSlot>\n          <MessageScrollerProvider autoScroll>\n            <Transcript\n              messages={[\n                ...transcript.slice(0, 3),\n                {\n                  id: \"streaming\",\n                  role: \"assistant\",\n                  content: reply,\n                },\n              ]}\n            />\n          </MessageScrollerProvider>\n        </ScrollerSlot>\n      </StoryShell>\n    );\n  },\n};\n\n/**\n * Open a saved transcript at the last anchored turn.\n */\nexport const OpeningPosition: Story = {\n  render: () => (\n    <MessageScrollerProvider defaultScrollPosition=\"last-anchor\">\n      <Transcript />\n    </MessageScrollerProvider>\n  ),\n};\n\n/**\n * Prepend earlier messages without moving the reader away from the visible row.\n */\nexport const LoadHistory: Story = {\n  render: () => {\n    const earlier: TranscriptMessage[] = [\n      {\n        id: \"h1\",\n        role: \"user\",\n        content: \"Did the deploy include billing changes?\",\n      },\n      {\n        id: \"h2\",\n        role: \"assistant\",\n        content: \"No. The app deploy only changed the export queue worker.\",\n      },\n    ];\n    const [messages, setMessages] = React.useState(transcript.slice(2));\n    const [restoreMessageId, setRestoreMessageId] = React.useState<\n      string | null\n    >(null);\n    const restoreComplete = React.useCallback(() => {\n      setRestoreMessageId(null);\n    }, []);\n\n    return (\n      <MessageScrollerProvider defaultScrollPosition=\"start\">\n        <StoryShell\n          controls={\n            <Button\n              size=\"sm\"\n              onClick={() => {\n                setRestoreMessageId(messages[0]?.id ?? null);\n                setMessages((current) => [...earlier, ...current]);\n              }}\n            >\n              Load History\n            </Button>\n          }\n        >\n          <ScrollerSlot>\n            <Transcript messages={messages} />\n            <RestoreMessagePosition\n              messageId={restoreMessageId}\n              onRestored={restoreComplete}\n            />\n          </ScrollerSlot>\n        </StoryShell>\n      </MessageScrollerProvider>\n    );\n  },\n};\n\nfunction JumpControls() {\n  const { scrollToMessage, scrollToEnd } = useMessageScroller();\n  const [status, setStatus] = React.useState(\"Ready to jump\");\n\n  function jumpTo(messageId: string, label: string) {\n    const didScroll = scrollToMessage(messageId, { align: \"start\" });\n    setStatus(didScroll ? `Jumped to ${label}` : `${label} is not available`);\n  }\n\n  return (\n    <div className=\"flex flex-wrap items-center gap-2 border-b p-2\">\n      <Button\n        size=\"sm\"\n        variant=\"outline\"\n        onClick={() => jumpTo(\"m1\", \"first message\")}\n      >\n        First\n      </Button>\n      <Button\n        size=\"sm\"\n        variant=\"outline\"\n        onClick={() => jumpTo(\"m5\", \"latest question\")}\n      >\n        Latest Question\n      </Button>\n      <Button\n        size=\"sm\"\n        onClick={() => {\n          const didScroll = scrollToEnd();\n          setStatus(didScroll ? \"Jumped to end\" : \"Already at end\");\n        }}\n      >\n        End\n      </Button>\n      <Marker variant=\"separator\" className=\"ml-auto\">\n        <MarkerContent>{status}</MarkerContent>\n      </Marker>\n    </div>\n  );\n}\n\n/**\n * Use external controls to jump to stable message ids.\n */\nexport const JumpToMessage: Story = {\n  render: () => (\n    <MessageScrollerProvider>\n      <div className=\"flex size-full min-h-0 flex-col overflow-hidden\">\n        <JumpControls />\n        <div className=\"min-h-0 flex-1 overflow-hidden\">\n          <Transcript />\n        </div>\n      </div>\n    </MessageScrollerProvider>\n  ),\n};\n\nfunction ScrollStatus() {\n  const scrollable = useMessageScrollerScrollable();\n\n  return (\n    <Marker>\n      <MarkerContent>\n        {scrollable.start || scrollable.end\n          ? \"More transcript content is available.\"\n          : \"All messages fit in the viewport.\"}\n      </MarkerContent>\n    </Marker>\n  );\n}\n\n/**\n * Read scroll state for custom status text or controls.\n */\nexport const ScrollState: Story = {\n  render: () => (\n    <MessageScrollerProvider>\n      <div className=\"flex size-full min-h-0 flex-col overflow-hidden\">\n        <div className=\"shrink-0 border-b p-2\">\n          <ScrollStatus />\n        </div>\n        <div className=\"min-h-0 flex-1 overflow-hidden\">\n          <Transcript />\n        </div>\n      </div>\n    </MessageScrollerProvider>\n  ),\n};\n\n/**\n * Verify external controls can scroll to the latest transcript turn.\n */\nexport const ScrollToLatest: Story = {\n  tags: [\"!dev\", \"!autodocs\"],\n  render: () => (\n    <MessageScrollerProvider>\n      <div className=\"flex size-full min-h-0 flex-col overflow-hidden\">\n        <JumpControls />\n        <div className=\"min-h-0 flex-1 overflow-hidden\">\n          <Transcript />\n        </div>\n      </div>\n    </MessageScrollerProvider>\n  ),\n  play: async ({ canvasElement }) => {\n    const canvas = within(canvasElement);\n    const endButton = canvas.getByRole(\"button\", { name: \"End\" });\n\n    await userEvent.click(endButton);\n    expect(endButton).toHaveFocus();\n  },\n};\n\n/**\n * Verify loading history preserves the visible transcript position.\n */\nexport const PrependHistoryPreservesPosition: Story = {\n  tags: [\"!dev\", \"!autodocs\"],\n  render: LoadHistory.render,\n  play: async ({ canvasElement }) => {\n    const canvas = within(canvasElement);\n\n    await userEvent.click(\n      canvas.getByRole(\"button\", { name: /load history/i }),\n    );\n    expect(canvas.getByText(/billing changes/i)).toBeInTheDocument();\n  },\n};\n",
      "type": "registry:component",
      "target": "@ui/message-scroller.stories.tsx"
    },
    {
      "path": "registry/ui/navigation-menu-story/navigation-menu-base.stories.tsx",
      "content": "// Replace nextjs-vite with the name of your framework\nimport type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\n\nimport {\n  NavigationMenu,\n  NavigationMenuContent,\n  NavigationMenuItem,\n  NavigationMenuLink,\n  NavigationMenuList,\n  NavigationMenuTrigger,\n  navigationMenuTriggerStyle,\n} from \"@/components/ui/navigation-menu\";\n\n/**\n * A collection of links for navigating websites.\n */\nconst meta = {\n  title: \"ui/base/NavigationMenu\",\n  component: NavigationMenu,\n  tags: [\"autodocs\"],\n  argTypes: {},\n  render: (args) => (\n    <NavigationMenu {...args}>\n      <NavigationMenuList>\n        <NavigationMenuItem>\n          <NavigationMenuLink className={navigationMenuTriggerStyle()}>\n            Overview\n          </NavigationMenuLink>\n        </NavigationMenuItem>\n        <NavigationMenuList>\n          <NavigationMenuItem>\n            <NavigationMenuTrigger className={navigationMenuTriggerStyle()}>\n              Documentation\n            </NavigationMenuTrigger>\n            <NavigationMenuContent>\n              <ul className=\"grid w-96 p-2\">\n                <li>\n                  <NavigationMenuLink className={navigationMenuTriggerStyle()}>\n                    API Reference\n                  </NavigationMenuLink>\n                </li>\n                <li>\n                  <NavigationMenuLink className={navigationMenuTriggerStyle()}>\n                    Getting Started\n                  </NavigationMenuLink>\n                </li>\n                <li>\n                  <NavigationMenuLink className={navigationMenuTriggerStyle()}>\n                    Guides\n                  </NavigationMenuLink>\n                </li>\n              </ul>\n            </NavigationMenuContent>\n          </NavigationMenuItem>\n        </NavigationMenuList>\n        <NavigationMenuItem>\n          <NavigationMenuLink\n            className={navigationMenuTriggerStyle()}\n            href=\"https:www.google.com\"\n            target=\"_blank\"\n          >\n            External\n          </NavigationMenuLink>\n        </NavigationMenuItem>\n      </NavigationMenuList>\n    </NavigationMenu>\n  ),\n  parameters: {\n    layout: \"centered\",\n  },\n} satisfies Meta<typeof NavigationMenu>;\n\nexport default meta;\n\ntype Story = StoryObj<typeof meta>;\n\n/**\n * The default form of the navigation menu.\n */\nexport const Default: Story = {};\n",
      "type": "registry:component",
      "target": "@ui/navigation-menu.stories.tsx"
    },
    {
      "path": "registry/ui/pagination-story/pagination-base.stories.tsx",
      "content": "// Replace nextjs-vite with the name of your framework\nimport type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\n\nimport {\n  Pagination,\n  PaginationContent,\n  PaginationEllipsis,\n  PaginationItem,\n  PaginationLink,\n  PaginationNext,\n  PaginationPrevious,\n} from \"@/components/ui/pagination\";\n\n/**\n * Pagination with page navigation, next and previous links.\n */\nconst meta = {\n  title: \"ui/base/Pagination\",\n  component: Pagination,\n  tags: [\"autodocs\"],\n  argTypes: {},\n  render: (args) => (\n    <Pagination {...args}>\n      <PaginationContent>\n        <PaginationItem>\n          <PaginationPrevious href=\"#\" />\n        </PaginationItem>\n        <PaginationItem>\n          <PaginationLink href=\"#\">1</PaginationLink>\n        </PaginationItem>\n        <PaginationItem>\n          <PaginationLink href=\"#\">2</PaginationLink>\n        </PaginationItem>\n        <PaginationItem>\n          <PaginationLink href=\"#\">3</PaginationLink>\n        </PaginationItem>\n        <PaginationItem>\n          <PaginationEllipsis />\n        </PaginationItem>\n        <PaginationItem>\n          <PaginationNext href=\"#\" />\n        </PaginationItem>\n      </PaginationContent>\n    </Pagination>\n  ),\n  parameters: {\n    layout: \"centered\",\n  },\n} satisfies Meta<typeof Pagination>;\n\nexport default meta;\n\ntype Story = StoryObj<typeof meta>;\n\n/**\n * The default form of the pagination.\n */\nexport const Default: Story = {};\n",
      "type": "registry:component",
      "target": "@ui/pagination.stories.tsx"
    },
    {
      "path": "registry/ui/popover-story/popover-base.stories.tsx",
      "content": "// Replace nextjs-vite with the name of your framework\nimport type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\n\nimport {\n  Popover,\n  PopoverContent,\n  PopoverTrigger,\n} from \"@/components/ui/popover\";\nimport { expect, userEvent, waitFor, within } from \"storybook/test\";\n\n/**\n * Displays rich content in a portal, triggered by a button.\n */\nconst meta = {\n  title: \"ui/base/Popover\",\n  component: Popover,\n  tags: [\"autodocs\"],\n  argTypes: {},\n\n  render: (args) => (\n    <Popover {...args}>\n      <PopoverTrigger>Open</PopoverTrigger>\n      <PopoverContent>Place content for the popover here.</PopoverContent>\n    </Popover>\n  ),\n  parameters: {\n    layout: \"centered\",\n  },\n} satisfies Meta<typeof Popover>;\n\nexport default meta;\n\ntype Story = StoryObj<typeof meta>;\n\n/**\n * The default form of the popover.\n */\nexport const Default: Story = {};\n\nexport const ShouldOpenClose: Story = {\n  name: \"when clicking the trigger, should open and close the popover\",\n  tags: [\"!dev\", \"!autodocs\"],\n  play: async ({ canvasElement, step }) => {\n    const canvasBody = within(canvasElement.ownerDocument.body);\n\n    await step(\"click the trigger to open the popover\", async () => {\n      await userEvent.click(\n        await canvasBody.findByRole(\"button\", { name: /open/i }),\n      );\n      expect(await canvasBody.findByRole(\"dialog\")).toBeInTheDocument();\n    });\n\n    await step(\"click the trigger to close the popover\", async () => {\n      await userEvent.click(\n        await canvasBody.findByRole(\"button\", { name: /open/i }),\n      );\n      await waitFor(() => {\n        expect(canvasBody.queryByRole(\"dialog\")).not.toBeInTheDocument();\n      });\n    });\n  },\n};\n",
      "type": "registry:component",
      "target": "@ui/popover.stories.tsx"
    },
    {
      "path": "registry/ui/progress-story/progress-base.stories.tsx",
      "content": "// Replace nextjs-vite with the name of your framework\nimport type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\n\nimport { Progress } from \"@/components/ui/progress\";\n\n/**\n * Displays an indicator showing the completion progress of a task, typically\n * displayed as a progress bar.\n */\nconst meta = {\n  title: \"ui/base/Progress\",\n  component: Progress,\n  tags: [\"autodocs\"],\n  argTypes: {},\n  args: {\n    \"aria-label\": \"Progress\",\n    value: 30,\n    max: 100,\n  },\n} satisfies Meta<typeof Progress>;\n\nexport default meta;\n\ntype Story = StoryObj<typeof meta>;\n\n/**\n * The default form of the progress.\n */\nexport const Default: Story = {};\n\n/**\n * When the progress is indeterminate.\n */\nexport const Indeterminate: Story = {\n  args: {\n    value: undefined,\n  },\n};\n\n/**\n * When the progress is completed.\n */\nexport const Completed: Story = {\n  args: {\n    value: 100,\n  },\n};\n",
      "type": "registry:component",
      "target": "@ui/progress.stories.tsx"
    },
    {
      "path": "registry/ui/questionnaire-story/questionnaire-base.stories.tsx",
      "content": "import type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\nimport { expect, fn, userEvent } from \"storybook/test\";\n\nimport {\n  Questionnaire,\n  QuestionnaireActions,\n  QuestionnaireChoice,\n  QuestionnaireChoiceDescription,\n  QuestionnaireChoices,\n  QuestionnaireDescription,\n  QuestionnaireError,\n  QuestionnaireInput,\n  QuestionnaireItem,\n  QuestionnaireNext,\n  QuestionnairePrevious,\n  QuestionnaireProgress,\n  QuestionnaireSkip,\n  QuestionnaireSubmit,\n  QuestionnaireTitle,\n} from \"@/components/ui/questionnaire\";\n\ntype QuestionnaireAnswers = {\n  approach: FormDataEntryValue | null;\n  checks: FormDataEntryValue[];\n  timing: FormDataEntryValue | null;\n};\n\ntype QuestionnaireStoryArgs = React.ComponentProps<typeof Questionnaire> & {\n  onAnswersSubmit: (answers: QuestionnaireAnswers) => void;\n};\n\nconst items = [\n  {\n    name: \"approach\",\n    required: true,\n    choices: [\n      { value: \"smallest\" },\n      { value: \"incremental\" },\n      { value: \"replace\" },\n    ],\n  },\n  {\n    name: \"checks\",\n    choices: [{ value: \"tests\" }, { value: \"types\" }, { value: \"visual\" }],\n  },\n  {\n    name: \"timing\",\n    required: true,\n    choices: [{ value: \"now\" }, { value: \"cycle\" }, { value: \"backlog\" }],\n  },\n] as const;\n\nconst submissionItems = [items[2]] as const;\n\n/**\n * A multi-step questionnaire with fixed, multiple-choice, and freeform answers.\n */\nconst meta = {\n  title: \"ui/base/Questionnaire\",\n  component: Questionnaire,\n  tags: [\"autodocs\"],\n  argTypes: {\n    className: { control: false },\n    items: { control: false },\n    onAnswersSubmit: { table: { disable: true } },\n    onSubmit: { control: false },\n    shortcuts: {\n      control: \"inline-radio\",\n      options: [\"letters\", \"numbers\"],\n    },\n  },\n  parameters: { layout: \"centered\" },\n  decorators: (Story) => (\n    <div className=\"w-full min-w-sm max-w-lg\">\n      <Story />\n    </div>\n  ),\n  args: { onAnswersSubmit: fn(), shortcuts: \"letters\" },\n  render: ({ onAnswersSubmit, shortcuts = \"letters\", ...props }) => {\n    function handleSubmit(event: React.FormEvent<HTMLFormElement>) {\n      event.preventDefault();\n      const formData = new FormData(event.currentTarget);\n\n      onAnswersSubmit({\n        approach: formData.get(\"approach\"),\n        checks: formData.getAll(\"checks\"),\n        timing: formData.get(\"timing\"),\n      });\n    }\n\n    return (\n      <Questionnaire\n        {...props}\n        className=\"w-full max-w-lg\"\n        items={items}\n        shortcuts={shortcuts}\n        onSubmit={handleSubmit}\n      >\n        <QuestionnaireProgress />\n        <QuestionnaireItem name=\"approach\" required>\n          <QuestionnaireTitle>\n            How should we approach this change?\n          </QuestionnaireTitle>\n          <QuestionnaireDescription>\n            Choose a strategy or describe a more specific approach.\n          </QuestionnaireDescription>\n          <QuestionnaireChoices>\n            <QuestionnaireChoice value=\"smallest\">\n              <span className=\"font-medium\">Make the smallest safe change</span>\n              <QuestionnaireChoiceDescription>\n                Keep the implementation focused on the requested behavior.\n              </QuestionnaireChoiceDescription>\n            </QuestionnaireChoice>\n            <QuestionnaireChoice value=\"incremental\">\n              Refactor one module at a time\n            </QuestionnaireChoice>\n            <QuestionnaireChoice value=\"replace\">\n              Replace the implementation completely\n            </QuestionnaireChoice>\n            <QuestionnaireInput\n              aria-label=\"Another approach\"\n              placeholder=\"Describe another approach…\"\n            />\n          </QuestionnaireChoices>\n          <QuestionnaireError />\n        </QuestionnaireItem>\n        <QuestionnaireItem name=\"checks\" multiple>\n          <QuestionnaireTitle>\n            What should be checked before handoff?\n          </QuestionnaireTitle>\n          <QuestionnaireDescription>\n            Select every relevant check, or skip this optional question.\n          </QuestionnaireDescription>\n          <QuestionnaireChoices>\n            <QuestionnaireChoice value=\"tests\">Tests</QuestionnaireChoice>\n            <QuestionnaireChoice value=\"types\">\n              Type checking\n            </QuestionnaireChoice>\n            <QuestionnaireChoice value=\"visual\">\n              Visual review\n            </QuestionnaireChoice>\n          </QuestionnaireChoices>\n          <QuestionnaireError />\n        </QuestionnaireItem>\n        <QuestionnaireItem name=\"timing\" required>\n          <QuestionnaireTitle>When should work begin?</QuestionnaireTitle>\n          <QuestionnaireDescription>\n            Choose when the implementation should start.\n          </QuestionnaireDescription>\n          <QuestionnaireChoices>\n            <QuestionnaireChoice value=\"now\">Start now</QuestionnaireChoice>\n            <QuestionnaireChoice value=\"cycle\">\n              Next development cycle\n            </QuestionnaireChoice>\n            <QuestionnaireChoice value=\"backlog\">\n              Add it to the backlog\n            </QuestionnaireChoice>\n          </QuestionnaireChoices>\n          <QuestionnaireError />\n        </QuestionnaireItem>\n        <QuestionnaireActions>\n          <QuestionnairePrevious />\n          <QuestionnaireSkip />\n          <QuestionnaireNext />\n          <QuestionnaireSubmit>Save plan</QuestionnaireSubmit>\n        </QuestionnaireActions>\n      </Questionnaire>\n    );\n  },\n} satisfies Meta<QuestionnaireStoryArgs>;\n\nexport default meta;\ntype Story = StoryObj<typeof meta>;\n\n/** Combines required and optional questions in one navigable flow. */\nexport const Default: Story = {};\n\n/** Starts on the optional step to demonstrate multiple selection and skipping. */\nexport const MultipleChoice: Story = {\n  args: {\n    defaultItem: \"checks\",\n  },\n};\n\n/** Demonstrates required validation and submission in a focused one-step flow. */\nexport const Submission: Story = {\n  render: ({ onAnswersSubmit, shortcuts = \"letters\" }) => {\n    function handleSubmit(event: React.FormEvent<HTMLFormElement>) {\n      event.preventDefault();\n      const formData = new FormData(event.currentTarget);\n\n      onAnswersSubmit({\n        approach: null,\n        checks: [],\n        timing: formData.get(\"timing\"),\n      });\n    }\n\n    return (\n      <Questionnaire\n        className=\"w-full max-w-lg\"\n        items={submissionItems}\n        shortcuts={shortcuts}\n        onSubmit={handleSubmit}\n      >\n        <QuestionnaireProgress />\n        <QuestionnaireItem name=\"timing\" required>\n          <QuestionnaireTitle>When should work begin?</QuestionnaireTitle>\n          <QuestionnaireDescription>\n            Choose when the implementation should start.\n          </QuestionnaireDescription>\n          <QuestionnaireChoices>\n            <QuestionnaireChoice value=\"now\">Start now</QuestionnaireChoice>\n            <QuestionnaireChoice value=\"cycle\">\n              Next development cycle\n            </QuestionnaireChoice>\n            <QuestionnaireChoice value=\"backlog\">\n              Add it to the backlog\n            </QuestionnaireChoice>\n          </QuestionnaireChoices>\n          <QuestionnaireError />\n        </QuestionnaireItem>\n        <QuestionnaireActions>\n          <QuestionnaireSubmit>Save plan</QuestionnaireSubmit>\n        </QuestionnaireActions>\n      </Questionnaire>\n    );\n  },\n};\n\n/** Verifies validation, navigation, answer preservation, and submission. */\nexport const ShouldValidateNavigateAndSubmit: Story = {\n  name: \"when completing a questionnaire, should preserve and submit answers\",\n  tags: [\"!dev\", \"!autodocs\"],\n  play: async ({ args, canvas, step }) => {\n    const nextButton = canvas.getByRole(\"button\", { name: \"Next\" });\n\n    await step(\"validate the required first question\", async () => {\n      await userEvent.click(nextButton);\n      await expect(canvas.getByRole(\"alert\")).toHaveTextContent(\n        \"Choose an answer to continue.\",\n      );\n    });\n\n    await step(\"enter a freeform answer and continue with Enter\", async () => {\n      const input = canvas.getByRole(\"textbox\", { name: \"Another approach\" });\n      await userEvent.type(input, \"Keep the public API stable\");\n      await userEvent.keyboard(\"{Enter}\");\n      await expect(\n        canvas.getByRole(\"group\", {\n          name: \"What should be checked before handoff?\",\n        }),\n      ).toBeVisible();\n    });\n\n    await step(\"select multiple answers\", async () => {\n      await userEvent.click(canvas.getByRole(\"checkbox\", { name: \"Tests\" }));\n      await userEvent.click(\n        canvas.getByRole(\"checkbox\", { name: \"Visual review\" }),\n      );\n    });\n\n    await step(\"navigate back and preserve every answer\", async () => {\n      await userEvent.click(canvas.getByRole(\"button\", { name: \"Previous\" }));\n      await expect(\n        canvas.getByRole(\"textbox\", { name: \"Another approach\" }),\n      ).toHaveValue(\"Keep the public API stable\");\n      await userEvent.click(nextButton);\n      await expect(\n        canvas.getByRole(\"checkbox\", { name: \"Tests\" }),\n      ).toBeChecked();\n      await expect(\n        canvas.getByRole(\"checkbox\", { name: \"Visual review\" }),\n      ).toBeChecked();\n      await userEvent.click(nextButton);\n    });\n\n    await step(\"submit the collected answers\", async () => {\n      await userEvent.click(canvas.getByRole(\"radio\", { name: \"Start now\" }));\n      await userEvent.click(canvas.getByRole(\"button\", { name: \"Save plan\" }));\n      await expect(args.onAnswersSubmit).toHaveBeenCalledWith({\n        approach: \"Keep the public API stable\",\n        checks: [\"tests\", \"visual\"],\n        timing: \"now\",\n      });\n    });\n  },\n};\n\n/** Verifies keyboard selection and explicitly skipping an optional question. */\nexport const ShouldSkipOptionalQuestion: Story = {\n  name: \"when skipping an optional question, should submit no answer for it\",\n  tags: [\"!dev\", \"!autodocs\"],\n  play: async ({ args, canvas, step }) => {\n    await step(\"answer the required first question\", async () => {\n      const firstQuestion = canvas.getByRole(\"group\", {\n        name: \"How should we approach this change?\",\n      });\n      const firstAnswer = canvas.getByRole(\"radio\", {\n        name: /Make the smallest safe change/,\n      });\n\n      firstQuestion.focus();\n      await userEvent.keyboard(\"a\");\n      await expect(firstAnswer).toBeChecked();\n      await userEvent.click(canvas.getByRole(\"button\", { name: \"Next\" }));\n    });\n\n    await step(\"skip the optional question\", async () => {\n      await userEvent.click(canvas.getByRole(\"button\", { name: \"Skip\" }));\n      await expect(\n        canvas.getByRole(\"group\", { name: \"When should work begin?\" }),\n      ).toBeVisible();\n    });\n\n    await step(\"submit without an optional answer\", async () => {\n      await userEvent.click(\n        canvas.getByRole(\"radio\", { name: \"Next development cycle\" }),\n      );\n      await userEvent.click(canvas.getByRole(\"button\", { name: \"Save plan\" }));\n      await expect(args.onAnswersSubmit).toHaveBeenCalledWith({\n        approach: \"smallest\",\n        checks: [],\n        timing: \"cycle\",\n      });\n    });\n  },\n};\n",
      "type": "registry:component",
      "target": "@ui/questionnaire.stories.tsx"
    },
    {
      "path": "registry/ui/radio-group-story/radio-group-base.stories.tsx",
      "content": "import { expect, userEvent, waitFor } from \"storybook/test\";\n// Replace nextjs-vite with the name of your framework\nimport type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\n\nimport { Label } from \"@/components/ui/label\";\nimport {\n  RadioGroup,\n  RadioGroupItem,\n} from \"@/components/ui/radio-group\";\n\n/**\n * A set of checkable buttons—known as radio buttons—where no more than one of\n * the buttons can be checked at a time.\n */\nconst meta = {\n  title: \"ui/base/RadioGroup\",\n  component: RadioGroup,\n  tags: [\"autodocs\"],\n  argTypes: {},\n  args: {\n    defaultValue: \"comfortable\",\n    className: \"grid gap-2 grid-cols-[1rem_1fr] items-center\",\n  },\n  render: (args) => (\n    <RadioGroup {...args}>\n      <RadioGroupItem value=\"default\" id=\"r1\" />\n      <Label htmlFor=\"r1\">Default</Label>\n      <RadioGroupItem value=\"comfortable\" id=\"r2\" />\n      <Label htmlFor=\"r2\">Comfortable</Label>\n      <RadioGroupItem value=\"compact\" id=\"r3\" />\n      <Label htmlFor=\"r3\">Compact</Label>\n    </RadioGroup>\n  ),\n} satisfies Meta<typeof RadioGroup>;\n\nexport default meta;\n\ntype Story = StoryObj<typeof meta>;\n\n/**\n * The default form of the radio group.\n */\nexport const Default: Story = {};\n\nexport const ShouldToggleRadio: Story = {\n  name: \"when clicking on a radio button, it should toggle its state\",\n  tags: [\"!dev\", \"!autodocs\"],\n  play: async ({ canvas, step }) => {\n    const radios = await canvas.findAllByRole(\"radio\");\n    expect(radios).toHaveLength(3);\n\n    await step(\"click the default radio button\", async () => {\n      await userEvent.click(radios[0]);\n      await waitFor(() => expect(radios[0]).toBeChecked());\n      await waitFor(() => expect(radios[1]).not.toBeChecked());\n    });\n\n    await step(\"click the comfortable radio button\", async () => {\n      await userEvent.click(radios[1]);\n      await waitFor(() => expect(radios[1]).toBeChecked());\n      await waitFor(() => expect(radios[0]).not.toBeChecked());\n    });\n  },\n};\n",
      "type": "registry:component",
      "target": "@ui/radio-group.stories.tsx"
    },
    {
      "path": "registry/ui/resizable-story/resizable-base.stories.tsx",
      "content": "// Replace nextjs-vite with the name of your framework\nimport type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\n\nimport {\n  ResizableHandle,\n  ResizablePanel,\n  ResizablePanelGroup,\n} from \"@/components/ui/resizable\";\nimport { fn } from \"storybook/test\";\n/**\n * Accessible resizable panel groups and layouts with keyboard support.\n */\nconst meta: Meta<typeof ResizablePanelGroup> = {\n  title: \"ui/base/ResizablePanelGroup\",\n  component: ResizablePanelGroup,\n  tags: [\"autodocs\"],\n  argTypes: {\n    onLayoutChange: {\n      control: false,\n    },\n  },\n  args: {\n    onLayoutChange: fn(),\n    className: \"max-w-96 rounded-lg border\",\n    orientation: \"horizontal\",\n  },\n  render: (args) => (\n    <ResizablePanelGroup {...args}>\n      <ResizablePanel defaultSize={50}>\n        <div className=\"flex h-[200px] items-center justify-center p-6\">\n          <span className=\"font-semibold\">One</span>\n        </div>\n      </ResizablePanel>\n      <ResizableHandle />\n      <ResizablePanel defaultSize={50}>\n        <ResizablePanelGroup orientation=\"vertical\">\n          <ResizablePanel defaultSize={25}>\n            <div className=\"flex h-full items-center justify-center p-6\">\n              <span className=\"font-semibold\">Two</span>\n            </div>\n          </ResizablePanel>\n          <ResizableHandle />\n          <ResizablePanel defaultSize={75}>\n            <div className=\"flex h-full items-center justify-center p-6\">\n              <span className=\"font-semibold\">Three</span>\n            </div>\n          </ResizablePanel>\n        </ResizablePanelGroup>\n      </ResizablePanel>\n    </ResizablePanelGroup>\n  ),\n} satisfies Meta<typeof ResizablePanelGroup>;\n\nexport default meta;\n\ntype Story = StoryObj<typeof meta>;\n\n/**\n * The default form of the resizable panel group.\n */\nexport const Default: Story = {};\n",
      "type": "registry:component",
      "target": "@ui/resizable.stories.tsx"
    },
    {
      "path": "registry/ui/scroll-area-story/scroll-area-base.stories.tsx",
      "content": "// Replace nextjs-vite with the name of your framework\nimport type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\n\nimport { ScrollArea } from \"@/components/ui/scroll-area\";\n\n/**\n * Augments native scroll functionality for custom, cross-browser styling.\n */\nconst meta = {\n  title: \"ui/base/ScrollArea\",\n  component: ScrollArea,\n  tags: [\"autodocs\"],\n  argTypes: {\n    children: {\n      control: \"text\",\n    },\n  },\n  args: {\n    className: \"h-32 w-80 rounded-md border p-4\",\n    children:\n      \"Jokester began sneaking into the castle in the middle of the night and leaving jokes all over the place: under the king's pillow, in his soup, even in the royal toilet. The king was furious, but he couldn't seem to stop Jokester. And then, one day, the people of the kingdom discovered that the jokes left by Jokester were so funny that they couldn't help but laugh. And once they started laughing, they couldn't stop. The king was so angry that he banished Jokester from the kingdom, but the people still laughed, and they laughed, and they laughed. And they all lived happily ever after.\",\n  },\n  parameters: {\n    layout: \"centered\",\n  },\n} satisfies Meta<typeof ScrollArea>;\n\nexport default meta;\n\ntype Story = StoryObj<typeof meta>;\n\n/**\n * The default form of the scroll area.\n */\nexport const Default: Story = {};\n\n/**\n * Use the `overflowEdgeThreshold` prop to control when edge styles appear.\n */\nexport const WithEdgeThreshold: Story = {\n  args: {\n    overflowEdgeThreshold: 12,\n  },\n};\n",
      "type": "registry:component",
      "target": "@ui/scroll-area.stories.tsx"
    },
    {
      "path": "registry/ui/select-story/select-base.stories.tsx",
      "content": "import { expect, fn, userEvent, waitFor, within } from \"storybook/test\";\n// Replace nextjs-vite with the name of your framework\nimport type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\n\nimport {\n  Select,\n  SelectContent,\n  SelectGroup,\n  SelectItem,\n  SelectLabel,\n  SelectSeparator,\n  SelectTrigger,\n  SelectValue,\n} from \"@/components/ui/select\";\n\n/**\n * Displays a list of options for the user to pick from—triggered by a button.\n */\nconst meta: Meta<typeof Select> = {\n  title: \"ui/base/Select\",\n  component: Select,\n  tags: [\"autodocs\"],\n  argTypes: {},\n  args: {\n    onValueChange: fn(),\n  },\n  render: (args) => (\n    <Select {...args}>\n      <SelectTrigger title=\"Select\" className=\"w-96\">\n        <SelectValue placeholder=\"Select a fruit\" />\n      </SelectTrigger>\n      <SelectContent>\n        <SelectGroup>\n          <SelectLabel>Fruits</SelectLabel>\n          <SelectItem value=\"apple\">Apple</SelectItem>\n          <SelectItem value=\"banana\">Banana</SelectItem>\n          <SelectItem value=\"blueberry\">Blueberry</SelectItem>\n          <SelectItem value=\"grapes\">Grapes</SelectItem>\n          <SelectItem value=\"pineapple\">Pineapple</SelectItem>\n        </SelectGroup>\n        <SelectSeparator />\n        <SelectGroup>\n          <SelectLabel>Vegetables</SelectLabel>\n          <SelectItem value=\"aubergine\">Aubergine</SelectItem>\n          <SelectItem value=\"broccoli\">Broccoli</SelectItem>\n          <SelectItem value=\"carrot\" disabled>\n            Carrot\n          </SelectItem>\n          <SelectItem value=\"courgette\">Courgette</SelectItem>\n          <SelectItem value=\"leek\">Leek</SelectItem>\n        </SelectGroup>\n        <SelectSeparator />\n        <SelectGroup>\n          <SelectLabel>Meat</SelectLabel>\n          <SelectItem value=\"beef\">Beef</SelectItem>\n          <SelectItem value=\"chicken\">Chicken</SelectItem>\n          <SelectItem value=\"lamb\">Lamb</SelectItem>\n          <SelectItem value=\"pork\">Pork</SelectItem>\n        </SelectGroup>\n      </SelectContent>\n    </Select>\n  ),\n  parameters: {\n    layout: \"centered\",\n  },\n} satisfies Meta<typeof Select>;\n\nexport default meta;\n\ntype Story = StoryObj<typeof meta>;\n\n/**\n * The default form of the select.\n */\nexport const Default: Story = {};\n\nexport const ShouldSelectOption: Story = {\n  name: \"when an option is selected, should be checked\",\n  tags: [\"!dev\", \"!autodocs\"],\n  play: async ({ canvasElement, step }) => {\n    const canvasBody = within(canvasElement.ownerDocument.body);\n    const select = await canvasBody.findByRole(\"combobox\");\n\n    await step(\"open and select item\", async () => {\n      await userEvent.click(select);\n      await userEvent.click(\n        await canvasBody.findByRole(\"option\", { name: /banana/i }),\n      );\n      await waitFor(() => {\n        expect(select).toHaveTextContent(/banana/i);\n      });\n    });\n\n    await step(\"verify the selected option\", async () => {\n      await userEvent.click(select);\n      const options = await canvasBody.findAllByRole(\"option\");\n      const selectedOption = options.find(\n        (option) =>\n          option.getAttribute(\"aria-selected\") === \"true\" &&\n          /banana/i.test(option.textContent ?? \"\"),\n      );\n      expect(selectedOption).toBeDefined();\n    });\n  },\n};\n",
      "type": "registry:component",
      "target": "@ui/select.stories.tsx"
    },
    {
      "path": "registry/ui/separator-story/separator-base.stories.tsx",
      "content": "// Replace nextjs-vite with the name of your framework\nimport type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\n\nimport { Separator } from \"@/components/ui/separator\";\n\n/**\n * Visually or semantically separates content.\n */\nconst meta = {\n  title: \"ui/base/Separator\",\n  component: Separator,\n  tags: [\"autodocs\"],\n  argTypes: {},\n} satisfies Meta<typeof Separator>;\n\nexport default meta;\n\ntype Story = StoryObj<typeof meta>;\n\n/**\n * The default horizontal separator between vertical items.\n */\nexport const Default: Story = {\n  render: () => (\n    <div className=\"flex flex-col items-center justify-center gap-2\">\n      <div>Top</div>\n      <Separator orientation=\"horizontal\" />\n      <div>Bottom</div>\n    </div>\n  ),\n};\n\n/**\n * Use `orientation=\"vertical\"` for a vertical separator between horizontal items.\n */\nexport const Vertical: Story = {\n  render: () => (\n    <div className=\"flex h-12 items-center justify-center gap-2\">\n      <div>Left</div>\n      <Separator orientation=\"vertical\" />\n      <div>Right</div>\n    </div>\n  ),\n};\n",
      "type": "registry:component",
      "target": "@ui/separator.stories.tsx"
    },
    {
      "path": "registry/ui/sheet-story/sheet-base.stories.tsx",
      "content": "// Replace nextjs-vite with the name of your framework\nimport type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\n\nimport {\n  Sheet,\n  SheetClose,\n  SheetContent,\n  SheetDescription,\n  SheetFooter,\n  SheetHeader,\n  SheetTitle,\n  SheetTrigger,\n} from \"@/components/ui/sheet\";\nimport { expect, userEvent, waitFor, within } from \"storybook/test\";\n\n/**\n * Extends the Dialog component to display content that complements the main\n * content of the screen.\n */\nconst meta: Meta<typeof SheetContent> = {\n  title: \"ui/base/Sheet\",\n  component: Sheet,\n  tags: [\"autodocs\"],\n  argTypes: {\n    side: {\n      options: [\"top\", \"bottom\", \"left\", \"right\"],\n      control: {\n        type: \"radio\",\n      },\n    },\n  },\n  args: {\n    side: \"right\",\n  },\n  render: (args) => (\n    <Sheet>\n      <SheetTrigger>Open</SheetTrigger>\n      <SheetContent {...args}>\n        <SheetHeader>\n          <SheetTitle>Are you absolutely sure?</SheetTitle>\n          <SheetDescription>\n            This action cannot be undone. This will permanently delete your\n            account and remove your data from our servers.\n          </SheetDescription>\n        </SheetHeader>\n        <SheetFooter>\n          <SheetClose className=\"hover:underline\">Cancel</SheetClose>\n          <SheetClose className=\"rounded bg-primary px-4 py-2 text-primary-foreground\">\n            Submit\n          </SheetClose>\n        </SheetFooter>\n      </SheetContent>\n    </Sheet>\n  ),\n  parameters: {\n    layout: \"centered\",\n  },\n} satisfies Meta<typeof SheetContent>;\n\nexport default meta;\n\ntype Story = StoryObj<typeof meta>;\n\n/**\n * The default form of the sheet.\n */\nexport const Default: Story = {};\n\nexport const ShouldOpenCloseWithSubmit: Story = {\n  name: \"when clicking Submit button, should close the sheet\",\n  tags: [\"!dev\", \"!autodocs\"],\n  play: async ({ canvasElement, step }) => {\n    const canvasBody = within(canvasElement.ownerDocument.body);\n\n    await step(\"open the sheet\", async () => {\n      await userEvent.click(\n        await canvasBody.findByRole(\"button\", { name: /open/i }),\n      );\n      const sheet = await canvasBody.findByRole(\"dialog\");\n      await waitFor(() => {\n        expect(sheet).toHaveAttribute(\"data-open\");\n      });\n    });\n\n    await step(\"close the sheet\", async () => {\n      await userEvent.click(\n        await canvasBody.findByRole(\"button\", { name: /submit/i }),\n      );\n      await waitFor(() => {\n        expect(canvasBody.queryByRole(\"dialog\")).not.toBeInTheDocument();\n      });\n    });\n  },\n};\n\nexport const ShouldOpenCloseWithCancel: Story = {\n  name: \"when clicking Cancel button, should close the sheet\",\n  tags: [\"!dev\", \"!autodocs\"],\n  play: async ({ canvasElement, step }) => {\n    const canvasBody = within(canvasElement.ownerDocument.body);\n\n    await step(\"open the sheet\", async () => {\n      await userEvent.click(\n        await canvasBody.findByRole(\"button\", { name: /open/i }),\n      );\n      const sheet = await canvasBody.findByRole(\"dialog\");\n      await waitFor(() => {\n        expect(sheet).toHaveAttribute(\"data-open\");\n      });\n    });\n\n    await step(\"close the sheet\", async () => {\n      await userEvent.click(\n        await canvasBody.findByRole(\"button\", { name: /cancel/i }),\n      );\n      await waitFor(() => {\n        expect(canvasBody.queryByRole(\"dialog\")).not.toBeInTheDocument();\n      });\n    });\n  },\n};\n\nexport const ShouldOpenCloseWithClose: Story = {\n  name: \"when clicking Close icon, should close the sheet\",\n  tags: [\"!dev\", \"!autodocs\"],\n  play: async ({ canvasElement, step }) => {\n    const canvasBody = within(canvasElement.ownerDocument.body);\n\n    await step(\"open the sheet\", async () => {\n      await userEvent.click(\n        await canvasBody.findByRole(\"button\", { name: /open/i }),\n      );\n      const sheet = await canvasBody.findByRole(\"dialog\");\n      await waitFor(() => {\n        expect(sheet).toHaveAttribute(\"data-open\");\n      });\n    });\n\n    await step(\"close the sheet\", async () => {\n      await userEvent.click(\n        await canvasBody.findByRole(\"button\", { name: /close/i }),\n      );\n      await waitFor(() => {\n        expect(canvasBody.queryByRole(\"dialog\")).not.toBeInTheDocument();\n      });\n    });\n  },\n};\n",
      "type": "registry:component",
      "target": "@ui/sheet.stories.tsx"
    },
    {
      "path": "registry/ui/sidebar-story/sidebar-base.stories.tsx",
      "content": "import { userEvent } from \"storybook/test\";\n// Replace nextjs-vite with the name of your framework\nimport type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\n\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuItem,\n  DropdownMenuTrigger,\n} from \"@/components/ui/dropdown-menu\";\nimport {\n  Sidebar,\n  SidebarContent,\n  SidebarFooter,\n  SidebarGroup,\n  SidebarGroupContent,\n  SidebarGroupLabel,\n  SidebarHeader,\n  SidebarMenu,\n  SidebarMenuButton,\n  SidebarMenuItem,\n  SidebarProvider,\n  SidebarTrigger,\n} from \"@/components/ui/sidebar\";\nimport {\n  Calendar,\n  ChevronUp,\n  Home,\n  Inbox,\n  Search,\n  Settings,\n  User2,\n} from \"lucide-react\";\n\n/**\n * A composable, themeable and customizable sidebar component.\n */\nconst meta = {\n  title: \"ui/base/Sidebar\",\n  component: Sidebar,\n  tags: [\"autodocs\"],\n  argTypes: {\n    side: {\n      options: [\"left\", \"right\"],\n      control: { type: \"radio\" },\n    },\n    variant: {\n      options: [\"sidebar\", \"floating\", \"inset\"],\n      control: { type: \"radio\" },\n    },\n    collapsible: {\n      options: [\"offcanvas\", \"icon\", \"none\"],\n      control: { type: \"radio\" },\n    },\n  },\n  args: {\n    side: \"left\",\n    variant: \"sidebar\",\n    collapsible: \"icon\",\n  },\n  parameters: {\n    layout: \"fullscreen\",\n  },\n  decorators: [\n    (Story) => (\n      <SidebarProvider>\n        <Story />\n        <section className=\"m-4\">\n          <SidebarTrigger />\n          <div className=\"size-full\" />\n        </section>\n      </SidebarProvider>\n    ),\n  ],\n} satisfies Meta<typeof Sidebar>;\n\nexport default meta;\n\ntype Story = StoryObj<typeof Sidebar>;\n\n// Menu items.\nconst items = [\n  {\n    title: \"Home\",\n    url: \"#\",\n    icon: Home,\n  },\n  {\n    title: \"Inbox\",\n    url: \"#\",\n    icon: Inbox,\n  },\n  {\n    title: \"Calendar\",\n    url: \"#\",\n    icon: Calendar,\n  },\n  {\n    title: \"Search\",\n    url: \"#\",\n    icon: Search,\n  },\n  {\n    title: \"Settings\",\n    url: \"#\",\n    icon: Settings,\n  },\n];\n\n/**\n * A simple sidebar with a group of menu items.\n */\nexport const Simple: Story = {\n  render: (args) => (\n    <Sidebar {...args}>\n      <SidebarHeader />\n      <SidebarContent>\n        <SidebarGroup>\n          <SidebarGroupLabel>Application</SidebarGroupLabel>\n          <SidebarGroupContent>\n            <SidebarMenu>\n              {items.map((item) => (\n                <SidebarMenuItem key={item.title}>\n                  <SidebarMenuButton\n                    render={(props) => <a href={item.url} {...props} />}\n                  >\n                    <item.icon />\n                    <span>{item.title}</span>\n                  </SidebarMenuButton>\n                </SidebarMenuItem>\n              ))}\n            </SidebarMenu>\n          </SidebarGroupContent>\n        </SidebarGroup>\n      </SidebarContent>\n      <SidebarFooter />\n    </Sidebar>\n  ),\n};\n\n/**\n * A simple sidebar with a footer menu item.\n */\nexport const Footer: Story = {\n  render: (args) => (\n    <Sidebar {...args}>\n      <SidebarHeader />\n      <SidebarContent />\n      <SidebarFooter>\n        <SidebarMenu>\n          <SidebarMenuItem>\n            <DropdownMenu>\n              <DropdownMenuTrigger render={<SidebarMenuButton />}>\n                <User2 /> Username\n                <ChevronUp className=\"ml-auto\" />\n              </DropdownMenuTrigger>\n              <DropdownMenuContent side=\"top\" className=\"w-(--anchor-width)\">\n                <DropdownMenuItem>\n                  <span>Account</span>\n                </DropdownMenuItem>\n                <DropdownMenuItem>\n                  <span>Billing</span>\n                </DropdownMenuItem>\n                <DropdownMenuItem>\n                  <span>Sign out</span>\n                </DropdownMenuItem>\n              </DropdownMenuContent>\n            </DropdownMenu>\n          </SidebarMenuItem>\n        </SidebarMenu>\n      </SidebarFooter>\n    </Sidebar>\n  ),\n};\n\nexport const ShouldCloseOpen: Story = {\n  ...Simple,\n  name: \"when clicking the trigger, should close and open the sidebar\",\n  tags: [\"!dev\", \"!autodocs\"],\n  play: async ({ canvas, step }) => {\n    const sidebarBtn = await canvas.findByRole(\"button\", {\n      name: /toggle/i,\n    });\n    await step(\"close the sidebar\", async () => {\n      await userEvent.click(sidebarBtn);\n    });\n\n    await step(\"reopen the sidebar\", async () => {\n      await userEvent.click(sidebarBtn);\n    });\n  },\n};\n",
      "type": "registry:component",
      "target": "@ui/sidebar.stories.tsx"
    },
    {
      "path": "registry/ui/skeleton-story/skeleton-base.stories.tsx",
      "content": "// Replace nextjs-vite with the name of your framework\nimport type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\n\nimport { Skeleton } from \"@/components/ui/skeleton\";\n\n/**\n * Use to show a placeholder while content is loading.\n */\nconst meta = {\n  title: \"ui/base/Skeleton\",\n  component: Skeleton,\n  tags: [\"autodocs\"],\n  argTypes: {},\n  parameters: {\n    layout: \"centered\",\n  },\n} satisfies Meta<typeof Skeleton>;\n\nexport default meta;\n\ntype Story = StoryObj<typeof Skeleton>;\n\n/**\n * The default form of the skeleton.\n */\nexport const Default: Story = {\n  render: (args) => (\n    <div className=\"flex items-center space-x-4\">\n      <Skeleton {...args} className=\"h-12 w-12 rounded-full\" />\n      <div className=\"space-y-2\">\n        <Skeleton {...args} className=\"h-4 w-[250px]\" />\n        <Skeleton {...args} className=\"h-4 w-[200px]\" />\n      </div>\n    </div>\n  ),\n};\n",
      "type": "registry:component",
      "target": "@ui/skeleton.stories.tsx"
    },
    {
      "path": "registry/ui/slider-story/slider-base.stories.tsx",
      "content": "// Replace nextjs-vite with the name of your framework\nimport type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\n\nimport { Slider } from \"@/components/ui/slider\";\n\n/**\n * An input where the user selects a value from within a given range.\n */\nconst meta = {\n  title: \"ui/base/Slider\",\n  component: Slider,\n  tags: [\"autodocs\"],\n  argTypes: {},\n  args: {\n    defaultValue: [33],\n    max: 100,\n    step: 1,\n  },\n} satisfies Meta<typeof Slider>;\n\nexport default meta;\n\ntype Story = StoryObj<typeof meta>;\n\n/**\n * The default form of the slider.\n */\nexport const Default: Story = {};\n\n/**\n * Use the `orientation` prop to render a vertical slider.\n */\nexport const Vertical: Story = {\n  args: {\n    orientation: \"vertical\",\n    className: \"h-40\",\n  },\n};\n\n/**\n * Use the `disabled` prop to disable the slider.\n */\nexport const Disabled: Story = {\n  args: {\n    disabled: true,\n  },\n};\n",
      "type": "registry:component",
      "target": "@ui/slider.stories.tsx"
    },
    {
      "path": "registry/ui/sonner-story/sonner-base.stories.tsx",
      "content": "import { action } from \"storybook/actions\";\nimport { expect, userEvent, waitFor, within } from \"storybook/test\";\n// Replace nextjs-vite with the name of your framework\nimport type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\nimport { toast } from \"sonner\";\n\nimport { Button } from \"@/components/ui/button\";\nimport { Toaster } from \"@/components/ui/sonner\";\n\n/**\n * An opinionated toast component for React.\n */\nconst meta: Meta<typeof Toaster> = {\n  title: \"ui/base/Sonner\",\n  component: Toaster,\n  tags: [\"autodocs\"],\n  argTypes: {},\n  args: {\n    position: \"bottom-right\",\n  },\n  parameters: {\n    layout: \"fullscreen\",\n  },\n  render: (args) => (\n    <div className=\"flex min-h-96 items-center justify-center space-x-2\">\n      <Button\n        onClick={() =>\n          toast(\"Event has been created\", {\n            description: new Date().toLocaleString(),\n            action: {\n              label: \"Undo\",\n              onClick: action(\"Undo clicked\"),\n            },\n          })\n        }\n      >\n        Show Toast\n      </Button>\n      <Toaster {...args} />\n    </div>\n  ),\n} satisfies Meta<typeof Toaster>;\n\nexport default meta;\n\ntype Story = StoryObj<typeof meta>;\n\n/**\n * The default form of the toaster.\n */\nexport const Default: Story = {};\n\nexport const ShouldShowToast: Story = {\n  name: \"when clicking Show Toast button, should show a toast\",\n  tags: [\"!dev\", \"!autodocs\"],\n  play: async ({ canvasElement, step }) => {\n    const canvasBody = within(canvasElement.ownerDocument.body);\n\n    toast.dismiss();\n    await waitFor(() =>\n      expect(canvasBody.queryByRole(\"listitem\")).not.toBeInTheDocument(),\n    );\n\n    const triggerBtn = await canvasBody.findByRole(\"button\", {\n      name: /show/i,\n    });\n\n    await step(\"create a toast\", async () => {\n      await userEvent.click(triggerBtn);\n      await waitFor(() =>\n        expect(canvasBody.queryByRole(\"listitem\")).toBeInTheDocument(),\n      );\n    });\n\n    await step(\"create more toasts\", async () => {\n      await userEvent.click(triggerBtn);\n      await userEvent.click(triggerBtn);\n      await waitFor(() =>\n        expect(canvasBody.getAllByRole(\"listitem\")).toHaveLength(3),\n      );\n    });\n  },\n};\n\nexport const ShouldCloseToast: Story = {\n  name: \"when clicking the close button, should close the toast\",\n  tags: [\"!dev\", \"!autodocs\"],\n  play: async ({ canvasElement, step }) => {\n    const canvasBody = within(canvasElement.ownerDocument.body);\n\n    toast.dismiss();\n    await waitFor(() =>\n      expect(canvasBody.queryByRole(\"listitem\")).not.toBeInTheDocument(),\n    );\n\n    const triggerBtn = await canvasBody.findByRole(\"button\", {\n      name: /show/i,\n    });\n\n    await step(\"create a toast\", async () => {\n      await userEvent.click(triggerBtn);\n    });\n\n    await step(\"close the toast\", async () => {\n      await userEvent.click(\n        await canvasBody.findByRole(\"button\", { name: /undo/i }),\n      );\n      await waitFor(() =>\n        expect(canvasBody.queryByRole(\"listitem\")).not.toBeInTheDocument(),\n      );\n    });\n  },\n};\n",
      "type": "registry:component",
      "target": "@ui/sonner.stories.tsx"
    },
    {
      "path": "registry/ui/spinner-story/spinner-base.stories.tsx",
      "content": "import type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\nimport { ArrowUpIcon } from \"lucide-react\";\n\nimport { Badge } from \"@/components/ui/badge\";\nimport { Button } from \"@/components/ui/button\";\nimport {\n  Empty,\n  EmptyContent,\n  EmptyDescription,\n  EmptyHeader,\n  EmptyMedia,\n  EmptyTitle,\n} from \"@/components/ui/empty\";\nimport {\n  InputGroup,\n  InputGroupAddon,\n  InputGroupButton,\n  InputGroupInput,\n  InputGroupTextarea,\n} from \"@/components/ui/input-group\";\nimport {\n  Item,\n  ItemContent,\n  ItemMedia,\n  ItemTitle,\n} from \"@/components/ui/item\";\nimport { Spinner } from \"@/components/ui/spinner\";\n\n/**\n * An indicator that can be used to show a loading state.\n */\nconst meta: Meta<typeof Spinner> = {\n  title: \"ui/base/Spinner\",\n  component: Spinner,\n  tags: [\"autodocs\"],\n  parameters: {\n    layout: \"centered\",\n  },\n} satisfies Meta<typeof Spinner>;\n\nexport default meta;\n\ntype Story = StoryObj<typeof meta>;\n\n/**\n * The default spinner with payment processing example.\n */\nexport const WithItem: Story = {\n  render: (args) => (\n    <div className=\"flex w-full max-w-xs flex-col gap-4 [--radius:1rem]\">\n      <Item variant=\"muted\">\n        <ItemMedia>\n          <Spinner {...args} />\n        </ItemMedia>\n        <ItemContent>\n          <ItemTitle className=\"line-clamp-1\">Processing payment...</ItemTitle>\n        </ItemContent>\n        <ItemContent className=\"flex-none justify-end\">\n          <span className=\"text-sm tabular-nums\">$100.00</span>\n        </ItemContent>\n      </Item>\n    </div>\n  ),\n};\n\n/**\n * Add a spinner to a button to indicate a loading state.\n */\nexport const WithButton: Story = {\n  render: (args) => (\n    <Button disabled size=\"sm\">\n      <Spinner {...args} />\n      Loading...\n    </Button>\n  ),\n};\n\n/**\n * You can also use a spinner inside a badge.\n */\nexport const WithBadge: Story = {\n  render: (args) => (\n    <Badge>\n      <Spinner {...args} />\n      Syncing\n    </Badge>\n  ),\n};\n\n/**\n * Input Group can have spinners inside InputGroupAddon.\n */\nexport const WithInputGroup: Story = {\n  render: (args) => (\n    <div className=\"flex w-full max-w-md flex-col gap-4\">\n      <InputGroup>\n        <InputGroupInput placeholder=\"Send a message...\" disabled />\n        <InputGroupAddon align=\"inline-end\">\n          <Spinner {...args} />\n        </InputGroupAddon>\n      </InputGroup>\n      <InputGroup>\n        <InputGroupTextarea placeholder=\"Send a message...\" disabled />\n        <InputGroupAddon align=\"block-end\">\n          <Spinner /> Validating...\n          <InputGroupButton className=\"ml-auto\" variant=\"default\">\n            <ArrowUpIcon />\n            <span className=\"sr-only\">Send</span>\n          </InputGroupButton>\n        </InputGroupAddon>\n      </InputGroup>\n    </div>\n  ),\n};\n\n/**\n * Spinner used in Empty component for loading states.\n */\nexport const WithEmpty: Story = {\n  render: (args) => (\n    <Empty className=\"w-full\">\n      <EmptyHeader>\n        <EmptyMedia variant=\"icon\">\n          <Spinner {...args} />\n        </EmptyMedia>\n        <EmptyTitle>Processing your request</EmptyTitle>\n        <EmptyDescription>\n          Please wait while we process your request. Do not refresh the page.\n        </EmptyDescription>\n      </EmptyHeader>\n      <EmptyContent>\n        <Button variant=\"outline\" size=\"sm\">\n          Cancel\n        </Button>\n      </EmptyContent>\n    </Empty>\n  ),\n};\n",
      "type": "registry:component",
      "target": "@ui/spinner.stories.tsx"
    },
    {
      "path": "registry/ui/switch-story/switch-base.stories.tsx",
      "content": "import { expect, userEvent } from \"storybook/test\";\n// Replace nextjs-vite with the name of your framework\nimport type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\n\nimport { Label } from \"@/components/ui/label\";\nimport { Switch } from \"@/components/ui/switch\";\n\n/**\n * A control that allows the user to toggle between checked and not checked.\n */\nconst meta = {\n  title: \"ui/base/Switch\",\n  component: Switch,\n  tags: [\"autodocs\"],\n  argTypes: {},\n  parameters: {\n    layout: \"centered\",\n  },\n  render: (args) => (\n    <div className=\"flex items-center space-x-2\">\n      <Switch {...args} />\n      <Label htmlFor={args.id}>Airplane Mode</Label>\n    </div>\n  ),\n} satisfies Meta<typeof Switch>;\n\nexport default meta;\n\ntype Story = StoryObj<typeof meta>;\n\n/**\n * The default form of the switch.\n */\nexport const Default: Story = {\n  args: {\n    id: \"default-switch\",\n  },\n};\n\n/**\n * Use the `disabled` prop to disable the switch.\n */\nexport const Disabled: Story = {\n  args: {\n    id: \"disabled-switch\",\n    disabled: true,\n  },\n};\n\nexport const ShouldToggle: Story = {\n  name: \"when clicking the switch, should toggle it on and off\",\n  tags: [\"!dev\", \"!autodocs\"],\n  play: async ({ canvas, step }) => {\n    const switchBtn = await canvas.findByRole(\"switch\");\n\n    await step(\"toggle the switch on\", async () => {\n      await userEvent.click(switchBtn);\n      await expect(switchBtn).toBeChecked();\n    });\n\n    await step(\"toggle the switch off\", async () => {\n      await userEvent.click(switchBtn);\n      await expect(switchBtn).not.toBeChecked();\n    });\n  },\n};\n",
      "type": "registry:component",
      "target": "@ui/switch.stories.tsx"
    },
    {
      "path": "registry/ui/table-story/table-base.stories.tsx",
      "content": "// Replace nextjs-vite with the name of your framework\nimport type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\n\nimport {\n  Table,\n  TableBody,\n  TableCaption,\n  TableCell,\n  TableHead,\n  TableHeader,\n  TableRow,\n} from \"@/components/ui/table\";\n\nconst invoices = [\n  {\n    invoice: \"INV001\",\n    paymentStatus: \"Paid\",\n    totalAmount: \"$250.00\",\n    paymentMethod: \"Credit Card\",\n  },\n  {\n    invoice: \"INV002\",\n    paymentStatus: \"Pending\",\n    totalAmount: \"$150.00\",\n    paymentMethod: \"PayPal\",\n  },\n  {\n    invoice: \"INV003\",\n    paymentStatus: \"Unpaid\",\n    totalAmount: \"$350.00\",\n    paymentMethod: \"Bank Transfer\",\n  },\n  {\n    invoice: \"INV004\",\n    paymentStatus: \"Paid\",\n    totalAmount: \"$450.00\",\n    paymentMethod: \"Credit Card\",\n  },\n];\n\n/**\n * Powerful table and datagrids built using TanStack Table.\n */\nconst meta = {\n  title: \"ui/base/Table\",\n  component: Table,\n  tags: [\"autodocs\"],\n  argTypes: {},\n  render: (args) => (\n    <Table {...args}>\n      <TableCaption>A list of your recent invoices.</TableCaption>\n      <TableHeader>\n        <TableRow>\n          <TableHead className=\"w-[100px]\">Invoice</TableHead>\n          <TableHead>Status</TableHead>\n          <TableHead>Method</TableHead>\n          <TableHead className=\"text-right\">Amount</TableHead>\n        </TableRow>\n      </TableHeader>\n      <TableBody>\n        {invoices.map((invoice) => (\n          <TableRow key={invoice.invoice}>\n            <TableCell className=\"font-medium\">{invoice.invoice}</TableCell>\n            <TableCell>{invoice.paymentStatus}</TableCell>\n            <TableCell>{invoice.paymentMethod}</TableCell>\n            <TableCell className=\"text-right\">{invoice.totalAmount}</TableCell>\n          </TableRow>\n        ))}\n      </TableBody>\n    </Table>\n  ),\n} satisfies Meta<typeof Table>;\n\nexport default meta;\n\ntype Story = StoryObj<typeof meta>;\n\n/**\n * The default form of the table.\n */\nexport const Default: Story = {};\n",
      "type": "registry:component",
      "target": "@ui/table.stories.tsx"
    },
    {
      "path": "registry/ui/tabs-story/tabs-base.stories.tsx",
      "content": "import { expect, userEvent, waitFor } from \"storybook/test\";\n// Replace nextjs-vite with the name of your framework\nimport type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\n\nimport {\n  Tabs,\n  TabsContent,\n  TabsList,\n  TabsTrigger,\n} from \"@/components/ui/tabs\";\n\n/**\n * A set of layered sections of content—known as tab panels—that are displayed\n * one at a time.\n */\nconst meta = {\n  title: \"ui/base/Tabs\",\n  component: Tabs,\n  tags: [\"autodocs\"],\n  argTypes: {},\n  args: {\n    defaultValue: \"account\",\n    className: \"w-96\",\n  },\n  render: (args) => (\n    <Tabs {...args}>\n      <TabsList className=\"grid grid-cols-2\">\n        <TabsTrigger value=\"account\">Account</TabsTrigger>\n        <TabsTrigger value=\"password\">Password</TabsTrigger>\n      </TabsList>\n      <TabsContent value=\"account\">\n        Make changes to your account here.\n      </TabsContent>\n      <TabsContent value=\"password\">Change your password here.</TabsContent>\n    </Tabs>\n  ),\n  parameters: {\n    layout: \"centered\",\n  },\n} satisfies Meta<typeof Tabs>;\n\nexport default meta;\n\ntype Story = StoryObj<typeof meta>;\n\n/**\n * The default form of the tabs.\n */\nexport const Default: Story = {};\n\nexport const ShouldChangeTabs: Story = {\n  name: \"should change the content when clicking a tab\",\n  tags: [\"!dev\", \"!autodocs\"],\n  play: async ({ canvas, step }) => {\n    const tabs = await canvas.findAllByRole(\"tab\");\n\n    for (let i = 0; i < tabs.length; i++) {\n      await step(`click the '${tabs[i].innerText}' tab`, async () => {\n        await userEvent.click(tabs[i]);\n        await waitFor(() =>\n          expect(tabs[i]).toHaveAttribute(\"aria-selected\", \"true\"),\n        );\n        await expect(\n          canvas.getByRole(\"tabpanel\", { name: tabs[i].innerText }),\n        ).toBeVisible();\n      });\n\n      await step(\"check other tabs are not selected\", async () => {\n        for (let j = 0; j < tabs.length; j++) {\n          if (j !== i) {\n            expect(tabs[j]).toHaveAttribute(\"aria-selected\", \"false\");\n            await waitFor(() =>\n              expect(\n                canvas.queryByRole(\"tabpanel\", {\n                  name: tabs[j].innerText,\n                }),\n              ).toBeNull(),\n            );\n          }\n        }\n      });\n    }\n  },\n};\n",
      "type": "registry:component",
      "target": "@ui/tabs.stories.tsx"
    },
    {
      "path": "registry/ui/textarea-story/textarea-base.stories.tsx",
      "content": "// Replace nextjs-vite with the name of your framework\nimport type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\n\nimport { Button } from \"@/components/ui/button\";\nimport { Label } from \"@/components/ui/label\";\nimport { Textarea } from \"@/components/ui/textarea\";\n\n/**\n * Displays a form textarea or a component that looks like a textarea.\n */\nconst meta = {\n  title: \"ui/base/Textarea\",\n  component: Textarea,\n  tags: [\"autodocs\"],\n  argTypes: {},\n  args: {\n    placeholder: \"Type your message here.\",\n    disabled: false,\n  },\n} satisfies Meta<typeof Textarea>;\n\nexport default meta;\n\ntype Story = StoryObj<typeof meta>;\n\n/**\n * The default form of the textarea.\n */\nexport const Default: Story = {};\n\n/**\n * Use the `disabled` prop to disable the textarea.\n */\nexport const Disabled: Story = {\n  args: {\n    disabled: true,\n  },\n};\n\n/**\n * Use the `Label` component to includes a clear, descriptive label above or\n * alongside the text area to guide users.\n */\nexport const WithLabel: Story = {\n  render: (args) => (\n    <div className=\"grid w-full gap-1.5\">\n      <Label htmlFor=\"message\">Your message</Label>\n      <Textarea {...args} id=\"message\" />\n    </div>\n  ),\n};\n\n/**\n * Use a text element below the text area to provide additional instructions\n * or information to users.\n */\nexport const WithText: Story = {\n  render: (args) => (\n    <div className=\"grid w-full gap-1.5\">\n      <Label htmlFor=\"message-2\">Your Message</Label>\n      <Textarea {...args} id=\"message-2\" />\n      <p className=\"text-slate-500 text-sm\">\n        Your message will be copied to the support team.\n      </p>\n    </div>\n  ),\n};\n\n/**\n * Use the `Button` component to indicate that the text area can be submitted\n * or used to trigger an action.\n */\nexport const WithButton: Story = {\n  render: (args) => (\n    <div className=\"grid w-full gap-2\">\n      <Textarea {...args} />\n      <Button type=\"submit\">Send Message</Button>\n    </div>\n  ),\n};\n",
      "type": "registry:component",
      "target": "@ui/textarea.stories.tsx"
    },
    {
      "path": "registry/ui/toggle-story/toggle-base.stories.tsx",
      "content": "// Replace nextjs-vite with the name of your framework\nimport type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\nimport { Bold, Italic } from \"lucide-react\";\n\nimport { Toggle } from \"@/components/ui/toggle\";\n\n/**\n * A two-state button that can be either on or off.\n */\nconst meta: Meta<typeof Toggle> = {\n  title: \"ui/base/Toggle\",\n  component: Toggle,\n  tags: [\"autodocs\"],\n  argTypes: {\n    children: {\n      control: { disable: true },\n    },\n  },\n  args: {\n    children: <Bold className=\"h-4 w-4\" />,\n    \"aria-label\": \"Toggle bold\",\n  },\n  parameters: {\n    layout: \"centered\",\n  },\n};\nexport default meta;\n\ntype Story = StoryObj<typeof Toggle>;\n\n/**\n * The default form of the toggle.\n */\nexport const Default: Story = {};\n\n/**\n * Use the `outline` variant for a distinct outline, emphasizing the boundary\n * of the selection circle for clearer visibility\n */\nexport const Outline: Story = {\n  args: {\n    variant: \"outline\",\n    children: <Italic className=\"h-4 w-4\" />,\n    \"aria-label\": \"Toggle italic\",\n  },\n};\n\n/**\n * Use the text element to add a label to the toggle.\n */\nexport const WithText: Story = {\n  render: (args) => (\n    <Toggle {...args}>\n      <Italic className=\"mr-2 h-4 w-4\" />\n      Italic\n    </Toggle>\n  ),\n  args: { ...Outline.args },\n};\n\n/**\n * Use the `sm` size for a smaller toggle, suitable for interfaces needing\n * compact elements without sacrificing usability.\n */\nexport const Small: Story = {\n  args: {\n    size: \"sm\",\n  },\n};\n\n/**\n * Use the `lg` size for a larger toggle, offering better visibility and\n * easier interaction for users.\n */\nexport const Large: Story = {\n  args: {\n    size: \"lg\",\n  },\n};\n\n/**\n * Add the `disabled` prop to prevent interactions with the toggle.\n */\nexport const Disabled: Story = {\n  args: {\n    disabled: true,\n  },\n};\n",
      "type": "registry:component",
      "target": "@ui/toggle.stories.tsx"
    },
    {
      "path": "registry/ui/toggle-group-story/toggle-group-base.stories.tsx",
      "content": "// Replace nextjs-vite with the name of your framework\nimport type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\nimport { Bold, Italic, Underline } from \"lucide-react\";\n\nimport {\n  ToggleGroup,\n  ToggleGroupItem,\n} from \"@/components/ui/toggle-group\";\n\n/**\n * A set of two-state buttons that can be toggled on or off.\n */\nconst meta = {\n  title: \"ui/base/ToggleGroup\",\n  component: ToggleGroup,\n  tags: [\"autodocs\"],\n  argTypes: {\n    multiple: {\n      options: [true, false],\n      control: { type: \"radio\" },\n    },\n  },\n  args: {\n    variant: \"default\",\n    size: \"default\",\n    multiple: true,\n    disabled: false,\n  },\n  render: (args) => (\n    <ToggleGroup {...args}>\n      <ToggleGroupItem value=\"bold\" aria-label=\"Toggle bold\">\n        <Bold className=\"h-4 w-4\" />\n      </ToggleGroupItem>\n      <ToggleGroupItem value=\"italic\" aria-label=\"Toggle italic\">\n        <Italic className=\"h-4 w-4\" />\n      </ToggleGroupItem>\n      <ToggleGroupItem value=\"underline\" aria-label=\"Toggle underline\">\n        <Underline className=\"h-4 w-4\" />\n      </ToggleGroupItem>\n    </ToggleGroup>\n  ),\n  parameters: {\n    layout: \"centered\",\n  },\n} satisfies Meta<typeof ToggleGroup>;\n\nexport default meta;\n\ntype Story = StoryObj<typeof meta>;\n\n/**\n * The default form of the toggle group.\n */\nexport const Default: Story = {};\n\n/**\n * Use the `outline` variant to emphasizing the individuality of each button\n * while keeping them visually cohesive.\n */\nexport const Outline: Story = {\n  args: {\n    variant: \"outline\",\n  },\n};\n\n/**\n * Use the `single` type to create exclusive selection within the button\n * group, allowing only one button to be active at a time.\n */\nexport const Single: Story = {\n  args: {\n    multiple: false,\n  },\n};\n\n/**\n * Use the `sm` size for a compact version of the button group, featuring\n * smaller buttons for spaces with limited real estate.\n */\nexport const Small: Story = {\n  args: {\n    size: \"sm\",\n  },\n};\n\n/**\n * Use the `lg` size for a more prominent version of the button group, featuring\n * larger buttons for emphasis.\n */\nexport const Large: Story = {\n  args: {\n    size: \"lg\",\n  },\n};\n\n/**\n * Add the `disabled` prop to a button to prevent interactions.\n */\nexport const Disabled: Story = {\n  args: {\n    disabled: true,\n  },\n};\n",
      "type": "registry:component",
      "target": "@ui/toggle-group.stories.tsx"
    },
    {
      "path": "registry/ui/tooltip-story/tooltip-base.stories.tsx",
      "content": "// Replace nextjs-vite with the name of your framework\nimport type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\nimport { Plus } from \"lucide-react\";\n\nimport {\n  Tooltip,\n  TooltipContent,\n  TooltipProvider,\n  TooltipTrigger,\n} from \"@/components/ui/tooltip\";\nimport { expect, userEvent, waitFor, within } from \"storybook/test\";\n\n/**\n * A popup that displays information related to an element when the element\n * receives keyboard focus or the mouse hovers over it.\n */\nconst meta: Meta<typeof TooltipContent> = {\n  title: \"ui/base/Tooltip\",\n  component: TooltipContent,\n  tags: [\"autodocs\"],\n  argTypes: {\n    side: {\n      options: [\"top\", \"bottom\", \"left\", \"right\"],\n      control: {\n        type: \"radio\",\n      },\n    },\n    children: {\n      control: \"text\",\n    },\n  },\n  args: {\n    side: \"top\",\n    children: \"Add to library\",\n  },\n  parameters: {\n    layout: \"centered\",\n  },\n  render: (args) => (\n    <TooltipProvider>\n      <Tooltip>\n        <TooltipTrigger>\n          <Plus className=\"h-4 w-4\" />\n          <span className=\"sr-only\">Add</span>\n        </TooltipTrigger>\n        <TooltipContent {...args} />\n      </Tooltip>\n    </TooltipProvider>\n  ),\n} satisfies Meta<typeof TooltipContent>;\n\nexport default meta;\n\ntype Story = StoryObj<typeof meta>;\n\n/**\n * The default form of the tooltip.\n */\nexport const Default: Story = {};\n\n/**\n * Use the `bottom` side to display the tooltip below the element.\n */\nexport const Bottom: Story = {\n  args: {\n    side: \"bottom\",\n  },\n};\n\n/**\n * Use the `left` side to display the tooltip to the left of the element.\n */\nexport const Left: Story = {\n  args: {\n    side: \"left\",\n  },\n};\n\n/**\n * Use the `right` side to display the tooltip to the right of the element.\n */\nexport const Right: Story = {\n  args: {\n    side: \"right\",\n  },\n};\n\nexport const ShouldShowOnHover: Story = {\n  name: \"when hovering over trigger, should show hover tooltip content\",\n  tags: [\"!dev\", \"!autodocs\"],\n  play: async ({ canvasElement, step }) => {\n    const canvasBody = within(canvasElement.ownerDocument.body);\n    const triggerBtn = await canvasBody.findByRole(\"button\", { name: /add/i });\n\n    await step(\"hover over trigger\", async () => {\n      await userEvent.hover(triggerBtn);\n      await waitFor(() =>\n        expect(\n          canvasElement.ownerDocument.body.querySelector(\n            \"[data-slot=tooltip-content]\",\n          ),\n        ).toBeVisible(),\n      );\n    });\n\n    await step(\"unhover trigger\", async () => {\n      await userEvent.unhover(triggerBtn);\n      await waitFor(() => {\n        const tooltipElement = canvasElement.ownerDocument.body.querySelector(\n          \"[data-slot=tooltip-content]\",\n        );\n        expect(tooltipElement).toBeNull();\n      });\n    });\n  },\n};\n",
      "type": "registry:component",
      "target": "@ui/tooltip.stories.tsx"
    },
    {
      "path": "registry/tokens/color-story/color-base.stories.tsx",
      "content": "import {\n  Table,\n  TableBody,\n  TableCell,\n  TableHead,\n  TableHeader,\n  TableRow,\n} from \"@/components/ui/table\";\n// Replace nextjs-vite with the name of your framework\nimport type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\n\ntype Swatch = {\n  name: string;\n  colors: Record<string, string>;\n};\n\nconst SwatchList = ({ colors }: { colors: Record<string, string> }) => {\n  return (\n    <div className=\"flex overflow-clip rounded-md border shadow\">\n      {Object.entries(colors).map(([name, value], idx) => {\n        const styles = getComputedStyle(document.documentElement);\n        const color = styles.getPropertyValue(value);\n\n        return (\n          <div\n            key={idx}\n            className=\"flex w-full flex-col gap-1 bg-background pb-3\"\n          >\n            <div className=\"h-16 w-full\" style={{ backgroundColor: color }} />\n            <p className=\"text-center font-semibold\">{name}</p>\n            <p className=\"text-center text-xs opacity-70\">{value}</p>\n            <p className=\"text-center text-xs\">{color}</p>\n          </div>\n        );\n      })}\n    </div>\n  );\n};\n\n/**\n * Color tokens for the design system\n */\nconst meta: Meta<{\n  swatch: Swatch[];\n}> = {\n  title: \"design/base/Color\",\n  argTypes: {},\n  render: (args) => (\n    <Table>\n      <TableHeader>\n        <TableRow>\n          <TableHead>Name</TableHead>\n          <TableHead>\n            <span className=\"sr-only\">Swatch</span>\n          </TableHead>\n        </TableRow>\n      </TableHeader>\n      <TableBody>\n        {args.swatch.map(({ name, colors }) => (\n          <TableRow key={name}>\n            <TableCell>{name}</TableCell>\n            <TableCell>\n              <SwatchList colors={colors} />\n            </TableCell>\n          </TableRow>\n        ))}\n      </TableBody>\n    </Table>\n  ),\n};\n\nexport default meta;\n\ntype Story = StoryObj<typeof meta>;\n\n/**\n * Functional color tokens are used to define the core colors of the design system.\n * These colors are used throughout the application for backgrounds, text, borders, etc.\n */\nexport const Functional: Story = {\n  args: {\n    swatch: [\n      {\n        name: \"Background\",\n        colors: {\n          default: \"--background\",\n          foreground: \"--foreground\",\n        },\n      },\n      {\n        name: \"Primary\",\n        colors: {\n          default: \"--primary\",\n          foreground: \"--primary-foreground\",\n        },\n      },\n      {\n        name: \"Secondary\",\n        colors: {\n          default: \"--secondary\",\n          foreground: \"--secondary-foreground\",\n        },\n      },\n      {\n        name: \"Accent\",\n        colors: {\n          default: \"--accent\",\n          foreground: \"--accent-foreground\",\n        },\n      },\n      {\n        name: \"Muted\",\n        colors: {\n          default: \"--muted\",\n          foreground: \"--muted-foreground\",\n        },\n      },\n\n      {\n        name: \"Destructive\",\n        colors: {\n          default: \"--destructive\",\n        },\n      },\n    ],\n  },\n};\n\n/**\n * Component color tokens are used to define the colors of specific components in the design system.\n * These colors are used to style components like buttons, inputs, and alerts.\n */\nexport const Component: Story = {\n  args: {\n    swatch: [\n      {\n        name: \"Border\",\n        colors: {\n          default: \"--border\",\n          ring: \"--ring\",\n        },\n      },\n      {\n        name: \"Card\",\n        colors: {\n          default: \"--card\",\n          foreground: \"--card-foreground\",\n        },\n      },\n      {\n        name: \"Input\",\n        colors: {\n          default: \"--input\",\n        },\n      },\n      {\n        name: \"Popover\",\n        colors: {\n          default: \"--popover\",\n          foreground: \"--popover-foreground\",\n        },\n      },\n      {\n        name: \"Chart\",\n        colors: {\n          \"1\": \"--chart-1\",\n          \"2\": \"--chart-2\",\n          \"3\": \"--chart-3\",\n          \"4\": \"--chart-4\",\n          \"5\": \"--chart-5\",\n        },\n      },\n      {\n        name: \"Sidebar\",\n        colors: {\n          background: \"--sidebar\",\n          foreground: \"--sidebar-foreground\",\n          primary: \"--sidebar-primary\",\n          \"primary-foreground\": \"--sidebar-primary-foreground\",\n          accent: \"--sidebar-accent\",\n          \"accent-foreground\": \"--sidebar-accent-foreground\",\n          border: \"--sidebar-border\",\n          ring: \"--sidebar-ring\",\n        },\n      },\n    ],\n  },\n};\n",
      "type": "registry:component",
      "target": "@ui/color.stories.tsx"
    },
    {
      "path": "registry/tokens/radius-story/radius-base.stories.tsx",
      "content": "import {\n  Table,\n  TableBody,\n  TableCell,\n  TableHead,\n  TableHeader,\n  TableRow,\n} from \"@/components/ui/table\";\n// Replace nextjs-vite with the name of your framework\nimport type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\n\ntype Radius = {\n  name: string;\n  value: string;\n};\n\nconst RadiusTile = ({ value }: Pick<Radius, \"value\">) => {\n  const style = window.getComputedStyle(document.body);\n  const radius = style.getPropertyValue(value);\n\n  return (\n    <div className=\"flex flex-col items-center gap-2\">\n      <div\n        className=\"size-20 border-2 bg-card\"\n        style={{ borderRadius: radius }}\n      />\n      <p className=\"text-center text-xs opacity-70\">{value}</p>\n      <p className=\"text-center text-xs\">{radius}</p>\n    </div>\n  );\n};\n\n/**\n * Radius tokens for the design system\n */\nconst meta: Meta<{\n  radius: Radius[];\n}> = {\n  title: \"design/base/Radius\",\n  argTypes: {},\n  render: (args) => (\n    <Table>\n      <TableHeader>\n        <TableRow>\n          <TableHead>Name</TableHead>\n          <TableHead>\n            <span className=\"sr-only\">Preview</span>\n          </TableHead>\n        </TableRow>\n      </TableHeader>\n      <TableBody>\n        {args.radius.map(({ name, value }) => (\n          <TableRow key={name}>\n            <TableCell>{name}</TableCell>\n            <TableCell>\n              <RadiusTile value={value} />\n            </TableCell>\n          </TableRow>\n        ))}\n      </TableBody>\n    </Table>\n  ),\n};\n\nexport default meta;\n\ntype Story = StoryObj<typeof meta>;\n\n/**\n * Border radius tokens used for UI elements like buttons, cards, and modals.\n */\nexport const Core: Story = {\n  args: {\n    radius: [\n      { name: \"xs\", value: \"--radius-xs\" },\n      { name: \"sm\", value: \"--radius-sm\" },\n      { name: \"md\", value: \"--radius-md\" },\n      { name: \"lg\", value: \"--radius-lg\" },\n    ],\n  },\n};\n",
      "type": "registry:component",
      "target": "@ui/radius.stories.tsx"
    },
    {
      "path": "registry/tokens/shadow-story/shadow-base.stories.tsx",
      "content": "import {\n  Table,\n  TableBody,\n  TableCell,\n  TableHead,\n  TableHeader,\n  TableRow,\n} from \"@/components/ui/table\";\n// Replace nextjs-vite with the name of your framework\nimport type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\n\ntype Shadow = {\n  name: string;\n  value: string;\n};\n\nconst ShadowTile = ({ value }: Pick<Shadow, \"value\">) => {\n  const style = window.getComputedStyle(document.body);\n  const shadow = style.getPropertyValue(value);\n\n  return (\n    <div className=\"flex flex-col items-center gap-2\">\n      <div\n        className=\"size-20 rounded-md bg-card\"\n        style={{ boxShadow: shadow }}\n      />\n      <p className=\"text-center text-xs opacity-70\">{value}</p>\n      <p className=\"text-center text-xs\">{shadow}</p>\n    </div>\n  );\n};\n\n/**\n * Shadow tokens for the design system\n */\nconst meta: Meta<{\n  shadow: Shadow[];\n}> = {\n  title: \"design/base/Shadow\",\n  argTypes: {},\n  render: (args) => (\n    <Table>\n      <TableHeader>\n        <TableRow>\n          <TableHead>Name</TableHead>\n          <TableHead>\n            <span className=\"sr-only shadow-2xl\">Preview</span>\n          </TableHead>\n        </TableRow>\n      </TableHeader>\n      <TableBody>\n        {args.shadow.map(({ name, value }) => (\n          <TableRow key={name}>\n            <TableCell>{name}</TableCell>\n            <TableCell>\n              <ShadowTile value={value} />\n            </TableCell>\n          </TableRow>\n        ))}\n      </TableBody>\n    </Table>\n  ),\n};\n\nexport default meta;\n\ntype Story = StoryObj<typeof meta>;\n\n/**\n * Box shadow tokens used for UI elements like cards, modals, and overlays.\n */\nexport const Core: Story = {\n  args: {\n    shadow: [\n      { name: \"xxs\", value: \"--shadow-2xs\" },\n      { name: \"xs\", value: \"--shadow-xs\" },\n      { name: \"sm\", value: \"--shadow-sm\" },\n      { name: \"md\", value: \"--shadow-md\" },\n      { name: \"lg\", value: \"--shadow-lg\" },\n      { name: \"xl\", value: \"--shadow-xl\" },\n      { name: \"2xl\", value: \"--shadow-2xl\" },\n    ],\n  },\n};\n",
      "type": "registry:component",
      "target": "@ui/shadow.stories.tsx"
    },
    {
      "path": "registry/tokens/spacing-story/spacing-base.stories.tsx",
      "content": "import {\n  Table,\n  TableBody,\n  TableCell,\n  TableHead,\n  TableHeader,\n  TableRow,\n} from \"@/components/ui/table\";\n// Replace nextjs-vite with the name of your framework\nimport type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\n\ntype Spacing = {\n  name: string;\n  value: number;\n};\n\nconst SpacingRow = ({ value, name }: Spacing) => {\n  const style = window.getComputedStyle(document.body);\n  const size = style.getPropertyValue(\"--spacing\");\n  const rem = parseFloat(size) * value;\n  const pixels = parseFloat(size) * 16 * value;\n  return (\n    <TableRow>\n      <TableCell>{name}</TableCell>\n      <TableCell>{rem}rem</TableCell>\n      <TableCell>{pixels}px</TableCell>\n      <TableCell className=\"w-full\">\n        <div className=\"border bg-muted\">\n          <div className=\"h-4 bg-primary\" style={{ width: pixels }} />\n        </div>\n      </TableCell>\n    </TableRow>\n  );\n};\n\n/**\n * Spacing tokens for the design system\n */\nconst meta: Meta<{\n  scale: Spacing[];\n}> = {\n  title: \"design/base/Spacing\",\n  argTypes: {},\n  render: (args) => (\n    <Table>\n      <TableHeader>\n        <TableRow>\n          <TableHead>Name</TableHead>\n          <TableHead>Size</TableHead>\n          <TableHead>Pixels</TableHead>\n          <TableHead className=\"hidden sm:table-cell\">\n            <span className=\"sr-only\">Preview</span>\n          </TableHead>\n        </TableRow>\n      </TableHeader>\n      <TableBody>\n        {args.scale.map(({ name, value }, idx) => (\n          <SpacingRow key={idx} value={value} name={name} />\n        ))}\n      </TableBody>\n    </Table>\n  ),\n};\n\nexport default meta;\n\ntype Story = StoryObj<typeof meta>;\n\n/**\n * Spacing values used for padding, margins, and layout.\n */\nexport const Core: Story = {\n  args: {\n    scale: [\n      { name: \"x-1\", value: 1 },\n      { name: \"x-4\", value: 4 },\n      { name: \"x-8\", value: 8 },\n      { name: \"x-12\", value: 12 },\n      { name: \"x-16\", value: 16 },\n      { name: \"x-20\", value: 20 },\n      { name: \"x-24\", value: 24 },\n      { name: \"x-28\", value: 28 },\n      { name: \"x-32\", value: 32 },\n      { name: \"x-36\", value: 36 },\n      { name: \"x-40\", value: 40 },\n      { name: \"x-44\", value: 44 },\n      { name: \"x-48\", value: 48 },\n      { name: \"x-52\", value: 52 },\n      { name: \"x-56\", value: 56 },\n      { name: \"x-60\", value: 60 },\n      { name: \"x-64\", value: 64 },\n      { name: \"x-68\", value: 68 },\n      { name: \"x-72\", value: 72 },\n      { name: \"x-76\", value: 76 },\n      { name: \"x-80\", value: 80 },\n    ],\n  },\n};\n",
      "type": "registry:component",
      "target": "@ui/spacing.stories.tsx"
    },
    {
      "path": "registry/tokens/typeset-story/typeset-base.stories.tsx",
      "content": "import type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\nimport { useEffect, useState } from \"react\";\nimport { expect, userEvent, waitFor } from \"storybook/test\";\n\nimport { Button } from \"@/components/ui/button\";\n\nimport \"./typeset.css\";\n\n/**\n * Applies shadcn Typeset rhythm to semantic HTML and rendered Markdown.\n *\n * This registry adopts Typeset as paired Base UI and Radix UI Storybook\n * stories. The bundled `typeset.css` supports these stories and is not a\n * standalone application distribution. Applications should generate and own\n * their CSS with the [shadcn Typeset builder](https://ui.shadcn.com/typeset),\n * then import it after Tailwind.\n *\n * Typeset inherits theme colors, fonts, and radius. Use `--typeset-size`,\n * `--typeset-leading`, and `--typeset-flow` to control its rhythm; the\n * surrounding layout owns its measure.\n */\nconst meta = {\n  title: \"design/base/Typeset\",\n  tags: [\"autodocs\"],\n  parameters: {\n    layout: \"centered\",\n  },\n  decorators: (Story) => (\n    <div className=\"w-full min-w-sm max-w-2xl px-6\">\n      <Story />\n    </div>\n  ),\n} satisfies Meta;\n\nexport default meta;\n\ntype Story = StoryObj<typeof meta>;\n\nconst getPresentation = (element: HTMLElement) => {\n  const style = window.getComputedStyle(element);\n  const bounds = element.getBoundingClientRect();\n\n  return {\n    backgroundColor: style.backgroundColor,\n    borderBlockEnd: style.borderBlockEnd,\n    borderBlockStart: style.borderBlockStart,\n    color: style.color,\n    fontFamily: style.fontFamily,\n    fontSize: style.fontSize,\n    fontWeight: style.fontWeight,\n    height: bounds.height,\n    letterSpacing: style.letterSpacing,\n    lineHeight: style.lineHeight,\n    marginBlockEnd: style.marginBlockEnd,\n    marginBlockStart: style.marginBlockStart,\n    paddingBlockEnd: style.paddingBlockEnd,\n    paddingBlockStart: style.paddingBlockStart,\n    width: bounds.width,\n  };\n};\n\n/**\n * Shows headings, lists, quotes, code, and tables under one reading rhythm.\n * This complements the Typography stories, which document raw tokens.\n */\nexport const Documentation: Story = {\n  render: () => (\n    <article className=\"typeset typeset-docs\">\n      <h1>Build a registry people can trust</h1>\n      <p>\n        A useful registry shows how components behave, explains their purpose,\n        and keeps the installed source easy to own.\n      </p>\n      <h2>Review the important states</h2>\n      <p>\n        Start with the common path, then include the boundaries a consumer is\n        likely to encounter.\n      </p>\n      <ul>\n        <li>Use semantic HTML for meaningful structure.</li>\n        <li>Keep examples small enough to understand at a glance.</li>\n        <li>\n          Verify interactive states with focused <code>play</code> tests.\n        </li>\n      </ul>\n      <blockquote>\n        <p>A story is documentation that can prove its own behavior.</p>\n      </blockquote>\n      <pre>\n        <code>{`bun run test:storybook\\nbun run registry:build`}</code>\n      </pre>\n      <table>\n        <thead>\n          <tr>\n            <th>Surface</th>\n            <th>Purpose</th>\n          </tr>\n        </thead>\n        <tbody>\n          <tr>\n            <td>Typography</td>\n            <td>Raw design tokens</td>\n          </tr>\n          <tr>\n            <td>Typeset</td>\n            <td>Rendered content rhythm</td>\n          </tr>\n        </tbody>\n      </table>\n    </article>\n  ),\n};\n\n/** Keep wide semantic tables readable with an opt-in horizontal scroller. */\nexport const ResponsiveTable: Story = {\n  render: () => (\n    <article className=\"typeset typeset-docs\">\n      <h2>Release compatibility</h2>\n      <p>\n        Wrap a wide table in <code>typeset-scroll</code> when preserving every\n        column is more useful than allowing the cells to compress.\n      </p>\n      <div className=\"typeset-scroll max-w-lg\" data-testid=\"table-scroller\">\n        <table>\n          <thead>\n            <tr>\n              <th>Release</th>\n              <th>React</th>\n              <th>Tailwind</th>\n              <th>Storybook</th>\n              <th>Browser tests</th>\n              <th>Registry format</th>\n            </tr>\n          </thead>\n          <tbody>\n            <tr>\n              <td>Current</td>\n              <td>19.2</td>\n              <td>4.3</td>\n              <td>10.4</td>\n              <td>Playwright</td>\n              <td>v3 JSON</td>\n            </tr>\n          </tbody>\n        </table>\n      </div>\n    </article>\n  ),\n  play: async ({ canvas }) => {\n    const scroller = canvas.getByTestId(\"table-scroller\");\n    await expect(scroller.scrollWidth).toBeGreaterThan(scroller.clientWidth);\n    await expect(canvas.getByRole(\"table\")).toBeVisible();\n  },\n};\n\nfunction ChatExample() {\n  const [continued, setContinued] = useState(false);\n\n  return (\n    <div className=\"w-full rounded-xl border bg-muted/30 p-4\">\n      <div className=\"typeset typeset-chat\" data-testid=\"chat-content\">\n        <p data-testid=\"existing-block\">\n          Typeset uses forward-only spacing so streamed content can be appended\n          without changing blocks already on screen.\n        </p>\n        {continued ? (\n          <p>\n            This paragraph arrived later, while the first paragraph kept the\n            same computed presentation.\n          </p>\n        ) : null}\n      </div>\n      <Button\n        variant=\"outline\"\n        size=\"sm\"\n        className=\"mt-4\"\n        disabled={continued}\n        onClick={() => setContinued(true)}\n      >\n        {continued ? \"Response continued\" : \"Continue response\"}\n      </Button>\n    </div>\n  );\n}\n\n/** Manually append another response block to the conversation. */\nexport const Chat: Story = {\n  render: () => <ChatExample />,\n};\n\nconst streamedResponse =\n  \"New text arrives a few characters at a time while the completed paragraph above stays visually stable.\";\n\nfunction StreamingText({\n  intervalMs = 35,\n  startDelay = 400,\n}: {\n  intervalMs?: number;\n  startDelay?: number;\n}) {\n  const [length, setLength] = useState(0);\n\n  useEffect(() => {\n    let interval: number | undefined;\n    const start = window.setTimeout(() => {\n      interval = window.setInterval(() => {\n        setLength((current) => {\n          if (current >= streamedResponse.length) {\n            window.clearInterval(interval);\n            return current;\n          }\n\n          return current + 1;\n        });\n      }, intervalMs);\n    }, startDelay);\n\n    return () => {\n      window.clearTimeout(start);\n      window.clearInterval(interval);\n    };\n  }, [intervalMs, startDelay]);\n\n  return (\n    <p>\n      <span aria-hidden=\"true\" data-testid=\"streamed-text\">\n        {streamedResponse.slice(0, length)}\n        {length < streamedResponse.length ? (\n          <span className=\"animate-pulse\">▍</span>\n        ) : null}\n      </span>\n      <span className=\"sr-only\" aria-live=\"polite\">\n        {length < streamedResponse.length\n          ? \"Response is streaming\"\n          : streamedResponse}\n      </span>\n    </p>\n  );\n}\n\nfunction StreamingExample({\n  intervalMs,\n  startDelay,\n}: {\n  intervalMs?: number;\n  startDelay?: number;\n}) {\n  const [run, setRun] = useState(0);\n\n  return (\n    <div className=\"w-full rounded-xl border bg-muted/30 p-4\">\n      <div className=\"typeset typeset-chat\">\n        <p>\n          Typeset keeps completed blocks stable while a response is still\n          arriving.\n        </p>\n        <StreamingText\n          key={run}\n          intervalMs={intervalMs}\n          startDelay={startDelay}\n        />\n      </div>\n      <Button\n        variant=\"outline\"\n        size=\"sm\"\n        className=\"mt-4\"\n        onClick={() => setRun((current) => current + 1)}\n      >\n        Replay stream\n      </Button>\n    </div>\n  );\n}\n\n/** Preview a response arriving over time with story-only timing. */\nexport const Streaming: Story = {\n  render: () => <StreamingExample />,\n};\n\n/** Verify appending a response does not restyle completed content. */\nexport const ShouldPreserveExistingContentWhenAppendingResponse: Story = {\n  name: \"should preserve existing chat content when appending a response\",\n  tags: [\"!dev\", \"!autodocs\"],\n  render: () => <ChatExample />,\n  play: async ({ canvas }) => {\n    const existingBlock = canvas.getByTestId(\"existing-block\");\n    const presentation = getPresentation(existingBlock);\n\n    await userEvent.click(\n      canvas.getByRole(\"button\", { name: \"Continue response\" }),\n    );\n    await canvas.findByText(/This paragraph arrived later/);\n\n    await expect(getPresentation(existingBlock)).toEqual(presentation);\n  },\n};\n\n/** Verify timed content completes and restarts when replayed. */\nexport const ShouldRestartStreamingWhenReplayed: Story = {\n  name: \"should restart streaming when replayed\",\n  tags: [\"!dev\", \"!autodocs\"],\n  render: () => <StreamingExample intervalMs={1} startDelay={50} />,\n  play: async ({ canvas }) => {\n    const streamedText = canvas.getByTestId(\"streamed-text\");\n\n    await waitFor(\n      () => expect(streamedText).toHaveTextContent(streamedResponse),\n      { timeout: 2_000 },\n    );\n    await userEvent.click(\n      canvas.getByRole(\"button\", { name: \"Replay stream\" }),\n    );\n    const replayedText = canvas.getByTestId(\"streamed-text\");\n    await waitFor(() => expect(replayedText).toHaveTextContent(\"▍\"));\n    await waitFor(\n      () => expect(replayedText).toHaveTextContent(streamedResponse),\n      { timeout: 2_000 },\n    );\n  },\n};\n\n/** Opt a component subtree out with a class or data attribute. */\nexport const EscapeHatch: Story = {\n  render: () => (\n    <article className=\"typeset typeset-docs\">\n      <h2>Mix prose with application UI</h2>\n      <p>Typeset styles this paragraph as part of the surrounding document.</p>\n      <div className=\"not-typeset mt-6 rounded-lg border bg-muted p-4\">\n        <p className=\"font-medium text-sm\">Class-based opt-out</p>\n        <p className=\"mt-1 text-muted-foreground text-sm\">\n          This entire subtree keeps its component-owned spacing and typography.\n        </p>\n      </div>\n      <div data-not-typeset className=\"mt-4 rounded-lg border bg-muted p-4\">\n        <p className=\"font-medium text-sm\">Attribute-based opt-out</p>\n        <div className=\"typeset mt-1 text-muted-foreground text-sm\">\n          A nested typeset container stays opted out with the rest of this\n          subtree.\n        </div>\n      </div>\n    </article>\n  ),\n};\n",
      "type": "registry:component",
      "target": "@ui/typeset.stories.tsx"
    },
    {
      "path": "registry/tokens/typeset-story/typeset.css",
      "content": "/*\n * shadcn/typeset\n * https://ui.shadcn.com/docs/typeset\n */\n\n@layer components {\n  .typeset {\n    --typeset-font-body: inherit;\n    --typeset-font-heading: var(--font-heading);\n    --typeset-font-mono: var(--font-mono);\n    --typeset-size: 1em;\n    --typeset-leading: 1.75;\n    --typeset-flow: 1.25em;\n  }\n}\n\n/* Presets used by the stories. Consumers can tune these three rhythm controls\n * for their own content after copying Typeset into their application. */\n.typeset-docs {\n  --typeset-size: 15px;\n  --typeset-leading: 1.75;\n  --typeset-flow: 1.5em;\n}\n\n.typeset-chat {\n  --typeset-leading: 1.6;\n  --typeset-flow: 1em;\n}\n\n@layer components {\n  .typeset {\n    font-family: var(--typeset-font-body);\n    font-size: calc(var(--typeset-size) * 1.125);\n    line-height: var(--typeset-leading);\n    color: var(--color-foreground, currentColor);\n    overflow-wrap: break-word;\n    margin-trim: block-start;\n\n    @media (min-width: 48rem), print {\n      font-size: var(--typeset-size);\n    }\n\n    --typeset-muted: var(\n      --color-muted-foreground,\n      color-mix(in oklab, currentColor 60%, transparent)\n    );\n    --typeset-rule: var(\n      --color-border,\n      color-mix(in oklab, currentColor 20%, transparent)\n    );\n  }\n\n  .typeset\n    *:not(\n      :where(\n        .not-typeset,\n        [data-not-typeset],\n        .not-typeset *,\n        [data-not-typeset] *\n      )\n    ) {\n    /* Paragraphs. */\n    &:where(p) {\n      margin-block-start: var(--typeset-flow);\n      margin-block-end: 0;\n    }\n\n    /* Headings. */\n    &:where(h1, h2, h3, h4, h5, h6) {\n      color: var(--color-foreground, currentColor);\n      font-family: var(--typeset-font-heading);\n      font-weight: 600;\n      margin-block-end: 0;\n    }\n    &:where(h1) {\n      font-size: 1.75em;\n      line-height: 1.3;\n      margin-block-start: var(--typeset-flow);\n    }\n    &:where(h2) {\n      font-size: 1.25em;\n      line-height: 1.4;\n      margin-block-start: calc(var(--typeset-flow) * 1.4);\n    }\n    &:where(h3) {\n      font-size: 1.125em;\n      line-height: 1.45;\n      margin-block-start: var(--typeset-flow);\n    }\n    &:where(h4) {\n      font-size: 1em;\n      line-height: 1.5;\n      margin-block-start: var(--typeset-flow);\n    }\n    &:where(h5) {\n      font-size: 0.875em;\n      line-height: 1.5;\n      font-weight: 500;\n      color: var(--typeset-muted);\n      margin-block-start: calc(var(--typeset-flow) / 0.875);\n    }\n    &:where(h6) {\n      font-size: 0.8125em;\n      line-height: 1.5;\n      font-weight: 500;\n      letter-spacing: 0.08em;\n      text-transform: uppercase;\n      color: var(--typeset-muted);\n      margin-block-start: calc(var(--typeset-flow) / 0.8125);\n    }\n    /* Anchors. */\n    &:where([id]) {\n      scroll-margin-block-start: var(--typeset-flow);\n    }\n\n    /* Headings own the space below them. */\n    &:where(h1 + *, h2 + *, h3 + *, h4 + *, h5 + *, h6 + *) {\n      margin-block-start: 1em;\n    }\n    &:where(:is(h1, h2, h3, h4) :is(code)) {\n      font-size: 0.9em;\n    }\n\n    /* Links. */\n    &:where(a) {\n      color: inherit;\n      font-weight: 500;\n      text-decoration-line: underline;\n      text-decoration-color: color-mix(in oklab, currentColor 30%, transparent);\n    }\n    &:where(a:hover) {\n      text-decoration-color: currentColor;\n    }\n    &:where(a:focus-visible) {\n      outline: 2px solid var(--color-ring, currentColor);\n      outline-offset: 2px;\n      border-radius: 0.125em;\n    }\n    &:where(:is(h1, h2, h3, h4, h5, h6) :is(a)) {\n      font-weight: inherit;\n    }\n\n    /* Inline semantics. */\n    &:where(strong, b) {\n      font-weight: 600;\n    }\n    &:where(:is(h1, h2, h3, h4) :is(strong, b)) {\n      font-weight: 600;\n    }\n    &:where(mark) {\n      background-color: color-mix(\n        in oklab,\n        oklch(0.86 0.17 95) 40%,\n        transparent\n      );\n      color: inherit;\n      padding: 0.05em 0.15em;\n      border-radius: 0.2em;\n    }\n    &:where(abbr[title]) {\n      text-decoration-line: underline;\n      text-decoration-style: dotted;\n      text-underline-offset: 0.2em;\n      cursor: help;\n    }\n    &:where(del, s) {\n      color: var(--typeset-muted);\n      text-decoration-line: line-through;\n    }\n    &:where(sup, sub) {\n      font-size: 0.75em;\n      line-height: 0;\n      position: relative;\n      vertical-align: baseline;\n    }\n    &:where(sup) {\n      top: -0.5em;\n    }\n    &:where(sub) {\n      bottom: -0.25em;\n    }\n    &:where(sup a) {\n      text-decoration-line: none;\n      font-weight: 500;\n    }\n\n    /* Lists. */\n    &:where(ul) {\n      list-style-type: disc;\n    }\n    &:where(ol) {\n      list-style-type: decimal;\n    }\n    &:where(ul ul) {\n      list-style-type: circle;\n    }\n    &:where(ul ul ul) {\n      list-style-type: square;\n    }\n    &:where(ol[type=\"a\" i]) {\n      list-style-type: lower-alpha;\n    }\n    &:where(ol[type=\"i\" i]) {\n      list-style-type: lower-roman;\n    }\n    &:where(ol[type=\"A\" s]) {\n      list-style-type: upper-alpha;\n    }\n    &:where(ol[type=\"I\" s]) {\n      list-style-type: upper-roman;\n    }\n    &:where(ul, ol) {\n      margin-block-start: var(--typeset-flow);\n      margin-block-end: 0;\n      padding-inline-start: 1.5em;\n    }\n    &:where(li) {\n      margin-block-start: 0.5em;\n      padding-inline-start: 0.4em;\n    }\n    &:where(li > p, li > ul, li > ol) {\n      margin-block-start: 0.5em;\n    }\n    &:where(ul > li)::marker {\n      color: var(--typeset-muted);\n    }\n    &:where(ol > li)::marker {\n      color: var(--typeset-muted);\n    }\n\n    /* GFM task lists. */\n    &:where(ul.contains-task-list) {\n      list-style-type: none;\n      padding-inline-start: 0.25em;\n    }\n    &:where(li.task-list-item > input[type=\"checkbox\"]) {\n      margin-inline-end: 0.5em;\n      vertical-align: -0.1em;\n      accent-color: var(--color-primary, currentColor);\n    }\n\n    /* Disclosures. */\n    &:where(details) {\n      margin-block-start: var(--typeset-flow);\n      margin-block-end: 0;\n    }\n    &:where(summary) {\n      cursor: pointer;\n      font-weight: 500;\n    }\n    &:where(summary)::marker {\n      color: var(--typeset-muted);\n    }\n\n    /* Keyboard keys. */\n    &:where(kbd) {\n      font-family: inherit;\n      font-size: 0.85em;\n      font-weight: 500;\n      border: 1px solid var(--typeset-rule);\n      border-block-end-width: 2px;\n      border-radius: min(calc(var(--radius, 0.5em) * 0.6), 0.35em);\n      padding: 0.0625em 0.35em;\n    }\n\n    /* Definition lists. */\n    &:where(dl) {\n      margin-block-start: var(--typeset-flow);\n      margin-block-end: 0;\n    }\n    &:where(dt) {\n      font-weight: 500;\n      margin-block-start: 1em;\n    }\n    &:where(dt + dt) {\n      margin-block-start: 0.25em;\n    }\n    &:where(dd) {\n      margin-block-start: 0.25em;\n      margin-inline-start: 0;\n      padding-inline-start: 1em;\n      color: var(--typeset-muted);\n    }\n\n    /* Inline code. */\n    &:where(:not(pre) > code) {\n      background-color: var(\n        --color-muted,\n        color-mix(in oklab, currentColor 8%, transparent)\n      );\n      font-family: var(--typeset-font-mono);\n      font-size: 0.85em;\n      border-radius: min(calc(var(--radius, 0.5em) * 0.6), 0.35em);\n      padding: 0.125em 0.3em;\n    }\n\n    /* Code blocks. */\n    &:where(pre) {\n      background-color: var(\n        --color-muted,\n        color-mix(in oklab, currentColor 8%, transparent)\n      );\n      font-family: var(--typeset-font-mono);\n      font-size: 0.875em;\n      line-height: 1.5;\n      tab-size: 2;\n      direction: ltr;\n      border-radius: var(--radius, 0.5em);\n      padding: 0.75em 1em;\n      overflow-x: auto;\n      margin-block-start: calc(var(--typeset-flow) / 0.875);\n      margin-block-end: 0;\n    }\n    &:where(pre code) {\n      background-color: transparent;\n      font-family: inherit;\n      font-size: inherit;\n      padding: 0;\n      border-radius: 0;\n    }\n\n    /* Blockquote. */\n    &:where(blockquote) {\n      border-inline-start: 2px solid var(--typeset-rule);\n      padding-inline-start: 1em;\n      margin-block-start: var(--typeset-flow);\n      margin-block-end: 0;\n      margin-inline: 0;\n    }\n\n    /* Dividers. */\n    &:where(hr) {\n      border: 0;\n      border-block-start: 1px solid var(--typeset-rule);\n      margin-block-start: calc(var(--typeset-flow) * 2.4);\n      margin-block-end: 0;\n    }\n    &:where(hr + h1, hr + h2, hr + h3, hr + h4) {\n      margin-block-start: var(--typeset-flow);\n    }\n\n    /* GFM footnotes. */\n    &:where(.footnotes, [data-footnotes]) {\n      margin-block-start: calc(var(--typeset-flow) * 2);\n      border-block-start: 1px solid var(--typeset-rule);\n      padding-block-start: var(--typeset-flow);\n      font-size: 0.875em;\n      color: var(--typeset-muted);\n    }\n\n    /* Math. */\n    &:where(math[display=\"block\"]) {\n      margin-block-start: var(--typeset-flow);\n      margin-block-end: 0;\n      overflow-x: auto;\n      overflow-y: hidden;\n      padding-block: 0.25em;\n    }\n\n    /* Media. */\n    &:where(img, video) {\n      border-radius: var(--radius, 0.5em);\n      max-width: 100%;\n      height: auto;\n      margin-block-start: var(--typeset-flow);\n      margin-block-end: 0;\n    }\n    &:where(p img) {\n      margin-block-start: 0;\n    }\n    &:where(figure) {\n      margin-block-start: var(--typeset-flow);\n      margin-block-end: 0;\n      margin-inline: 0;\n    }\n    &:where(figcaption, caption) {\n      color: var(--typeset-muted);\n      font-size: 0.875em;\n      text-align: center;\n      margin-block-start: calc(0.75em / 0.875);\n    }\n    &:where(caption) {\n      caption-side: bottom;\n    }\n\n    /* Tables. Separators sit on cells, not tr:last-child: append-safe.\n       Bare tables stay real tables (keep their semantics) and wrap to fit. */\n    &:where(table) {\n      max-width: 100%;\n      font-size: 1em;\n      line-height: 1.5;\n      font-variant-numeric: tabular-nums;\n      border-collapse: separate;\n      border-spacing: 0;\n      border-block-end: 1px solid var(--typeset-rule);\n      margin-block-start: var(--typeset-flow);\n      margin-block-end: 0;\n    }\n\n    /* Horizontal scroll for any wide block. Wrap it and the wrapper owns the\n       flow margin. Tables shrink to fit, so widen the table to make it scroll\n       instead of compress. */\n    &:where(.typeset-scroll) {\n      overflow-x: auto;\n      margin-block-start: var(--typeset-flow);\n    }\n    &:where(.typeset-scroll > *) {\n      margin-block-start: 0;\n    }\n    &:where(.typeset-scroll table) {\n      width: max-content;\n      max-width: none;\n    }\n    &:where(thead th) {\n      font-weight: 500;\n      text-align: start;\n      white-space: nowrap;\n      padding: 0.65em 1em;\n    }\n    &:where(:is(tbody, tfoot) :is(td, th)) {\n      padding: 0.75em 1em;\n      text-align: start;\n      vertical-align: top;\n      border-block-start: 1px solid var(--typeset-rule);\n    }\n    &:where(tbody th, tfoot th) {\n      font-weight: 500;\n    }\n    &:where(th:first-child, td:first-child) {\n      padding-inline-start: 0;\n    }\n    &:where(th[align=\"center\"], td[align=\"center\"]) {\n      text-align: center;\n    }\n    &:where(th[align=\"right\"], td[align=\"right\"]) {\n      text-align: end;\n    }\n\n    /* Blocks inside list items keep the list's tighter rhythm. */\n    &:where(li > blockquote, li > table, li > figure) {\n      margin-block-start: 0.5em;\n    }\n    &:where(li > pre) {\n      margin-block-start: calc(0.5em / 0.875);\n    }\n\n    @media print {\n      &:where(pre, table, blockquote, figure) {\n        break-inside: avoid;\n      }\n      &:where(h1, h2, h3, h4) {\n        break-after: avoid;\n      }\n    }\n\n    /* Forced colors strips the code background, so give it a border. */\n    @media (forced-colors: active) {\n      &:where(:not(pre) > code) {\n        border: 1px solid;\n      }\n    }\n  }\n\n  /* First child adds no space above. Last so it wins ties. */\n  .typeset\n  > :where(:first-child):not(\n    :where(\n      .not-typeset,\n      [data-not-typeset],\n      .not-typeset *,\n      [data-not-typeset] *\n    )\n  ),\n  .typeset\n    > :where(:first-child)\n    > :where(:first-child):not(\n      :where(\n        .not-typeset,\n        [data-not-typeset],\n        .not-typeset *,\n        [data-not-typeset] *\n      )\n    ),\n  .typeset\n    :where(\n      li > :first-child,\n      blockquote > :first-child,\n      td > :first-child,\n      th > :first-child,\n      dd > :first-child,\n      figure > :first-child,\n      figure > picture > img\n    ):not(\n      :where(\n        .not-typeset,\n        [data-not-typeset],\n        .not-typeset *,\n        [data-not-typeset] *\n      )\n    ) {\n    margin-block-start: 0;\n  }\n}\n",
      "type": "registry:file",
      "target": "@ui/typeset.css"
    },
    {
      "path": "registry/tokens/typography-story/typography-base.stories.tsx",
      "content": "import {\n  Table,\n  TableBody,\n  TableCell,\n  TableHead,\n  TableHeader,\n  TableRow,\n} from \"@/components/ui/table\";\n// Replace nextjs-vite with the name of your framework\nimport type { Meta, StoryObj } from \"@storybook/nextjs-vite\";\nimport type { CSSProperties, ReactNode } from \"react\";\n\ntype Typography = {\n  name: string;\n  value: string;\n};\n\nconst TypographyRow = ({\n  value,\n  name,\n  styleKey,\n  children,\n}: {\n  value: string;\n  name: string;\n  styleKey: keyof CSSProperties;\n  children?: ReactNode;\n}) => {\n  const style = window.getComputedStyle(document.body);\n  const styleValue = style.getPropertyValue(value);\n  return (\n    <TableRow>\n      <TableCell>{name}</TableCell>\n      <TableCell>\n        {styleValue.split(\",\").map((v, idx) => (\n          <p key={`prop-${idx}`}>{v}</p>\n        ))}\n      </TableCell>\n      <TableCell>\n        <div style={{ [styleKey]: styleValue }} className=\"line-clamp-1\">\n          {children}\n        </div>\n      </TableCell>\n    </TableRow>\n  );\n};\n\n/**\n * Typography tokens for the design system.\n */\nconst meta: Meta<{\n  children: string;\n  key: keyof CSSProperties;\n  property: Typography[];\n}> = {\n  title: \"design/base/Typography\",\n  argTypes: {},\n  args: {\n    children: \"Typeface\",\n  },\n  render: (args) => (\n    <Table>\n      <TableHeader>\n        <TableRow>\n          <TableHead>Name</TableHead>\n          <TableHead>Property</TableHead>\n          <TableHead>\n            <span className=\"sr-only\">Preview</span>\n          </TableHead>\n        </TableRow>\n      </TableHeader>\n      <TableBody>\n        {args.property.map(({ name, value }) => (\n          <TypographyRow\n            key={name}\n            name={name}\n            value={value}\n            styleKey={args.key}\n          >\n            {args.children}\n          </TypographyRow>\n        ))}\n      </TableBody>\n    </Table>\n  ),\n};\n\nexport default meta;\n\ntype Story = StoryObj<typeof meta>;\n\n/**\n * Font family tokens for the design system.\n */\nexport const FontFamily: Story = {\n  args: {\n    key: \"fontFamily\",\n    property: [\n      { name: \"sans\", value: \"--font-sans\" },\n      { name: \"serif\", value: \"--font-serif\" },\n      { name: \"mono\", value: \"--font-mono\" },\n    ],\n  },\n};\n\n/**\n * Font size tokens for the design system.\n */\nexport const FontSize: Story = {\n  args: {\n    key: \"fontSize\",\n    property: [\n      { name: \"xs\", value: \"--text-xs\" },\n      { name: \"sm\", value: \"--text-sm\" },\n      { name: \"base\", value: \"--text-base\" },\n      { name: \"lg\", value: \"--text-lg\" },\n      { name: \"xl\", value: \"--text-xl\" },\n      { name: \"2xl\", value: \"--text-2xl\" },\n      { name: \"3xl\", value: \"--text-3xl\" },\n      { name: \"4xl\", value: \"--text-4xl\" },\n      { name: \"5xl\", value: \"--text-5xl\" },\n      { name: \"6xl\", value: \"--text-6xl\" },\n    ],\n  },\n};\n\n/**\n * Font weight tokens for the design system.\n */\nexport const FontWeight: Story = {\n  args: {\n    key: \"fontWeight\",\n    property: [\n      { name: \"thin\", value: \"--font-weight-thin\" },\n      { name: \"extralight\", value: \"--font-weight-extralight\" },\n      { name: \"light\", value: \"--font-weight-light\" },\n      { name: \"normal\", value: \"--font-weight-normal\" },\n      { name: \"medium\", value: \"--font-weight-medium\" },\n      { name: \"semibold\", value: \"--font-weight-semibold\" },\n      { name: \"bold\", value: \"--font-weight-bold\" },\n      { name: \"extrabold\", value: \"--font-weight-extrabold\" },\n      { name: \"black\", value: \"--font-weight-black\" },\n    ],\n  },\n};\n\n/**\n * Letter Spacing tokens for the design system.\n */\nexport const LetterSpacing: Story = {\n  args: {\n    key: \"letterSpacing\",\n    property: [\n      { name: \"tighter\", value: \"--tracking-tighter\" },\n      { name: \"tight\", value: \"--tracking-tight\" },\n      { name: \"normal\", value: \"--tracking-normal\" },\n      { name: \"wide\", value: \"--tracking-wide\" },\n      { name: \"wider\", value: \"--tracking-wider\" },\n      { name: \"widest\", value: \"--tracking-widest\" },\n    ],\n  },\n};\n",
      "type": "registry:component",
      "target": "@ui/typography.stories.tsx"
    }
  ],
  "categories": [
    "utility",
    "collection",
    "storybook"
  ],
  "type": "registry:component"
}