{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"toast","title":"Toast","description":"A stacked toast manager built on Base UI with promise helpers, inline actions, and optional expandable details.","files":[{"path":"ui/toast.tsx","type":"registry:ui","content":"\"use client\";\n\nimport { Toast as ToastPrimitive } from \"@base-ui/react/toast\";\nimport type { ToastObject } from \"@base-ui/react/toast\";\nimport {\n  IconAlertTriangle,\n  IconCheck,\n  IconChevronDown,\n  IconChevronUp,\n  IconCircleCheck,\n  IconCircleX,\n  IconCopy,\n  IconInfoCircle,\n  IconX,\n} from \"@tabler/icons-react\";\nimport {\n  type CSSProperties,\n  type MouseEvent,\n  type ReactNode,\n  useEffect,\n  useRef,\n  useState,\n} from \"react\";\n\nimport { buttonVariants } from \"@/components/ui/button\";\nimport { Spinner } from \"@/components/ui/spinner\";\nimport { cn } from \"@/lib/utils\";\n\nexport type ToastType = \"default\" | \"success\" | \"error\" | \"warning\" | \"info\" | \"loading\";\n\nexport type ToastPosition =\n  | \"top-left\"\n  | \"top-center\"\n  | \"top-right\"\n  | \"bottom-left\"\n  | \"bottom-center\"\n  | \"bottom-right\";\n\nexport interface ToastAction {\n  label: ReactNode;\n  onClick: (event: MouseEvent<HTMLButtonElement>) => void;\n}\n\nexport interface ToastExpandableLabels {\n  expand?: ReactNode;\n  collapse?: ReactNode;\n}\n\nexport interface ToastData {\n  type: ToastType;\n  icon?: ReactNode;\n  action?: ToastAction;\n  cancel?: ToastAction;\n  dismissible?: boolean;\n  closeButton?: boolean;\n  richColors?: boolean;\n  hideCopyButton?: boolean;\n  expandableContent?: ReactNode;\n  expandableLabels?: ToastExpandableLabels;\n  expandableDescriptionTrigger?: boolean;\n  actionLayout?: \"inline\" | \"stacked-end\";\n  actionVariant?: \"default\" | \"destructive\" | \"ghost\" | \"link\" | \"outline\" | \"secondary\";\n}\n\nexport interface ToastOptions extends Omit<ToastData, \"type\"> {\n  id?: string;\n  description?: ReactNode;\n  duration?: number;\n  onDismiss?: () => void;\n  onAutoClose?: () => void;\n}\n\nexport interface PromiseStateOption {\n  title?: ReactNode;\n  description?: ReactNode;\n}\n\nexport type PromiseState<T> =\n  | string\n  | PromiseStateOption\n  | ((value: T) => string | PromiseStateOption);\n\nexport interface PromiseToastOptions<T> {\n  loading: string | PromiseStateOption;\n  success: PromiseState<T>;\n  error: PromiseState<unknown>;\n}\n\ntype PromiseToastStateData = {\n  title?: ReactNode;\n  description?: ReactNode;\n  type: string;\n  data: ToastData;\n};\n\ntype MappedPromiseState<T> =\n  | string\n  | PromiseToastStateData\n  | ((value: T) => string | PromiseToastStateData);\n\nconst toastManager = ToastPrimitive.createToastManager<ToastData>();\nconst activeToastIds = new Set<string>();\n\n// Base UI exposes this subscription hook under a key with a leading space.\ntoastManager[\" subscribe\"]((event: { action: string; options?: { id?: string } }) => {\n  const id = event.options?.id;\n\n  if (!id) {\n    return;\n  }\n\n  if (event.action === \"add\") {\n    activeToastIds.add(id);\n  }\n  if (event.action === \"close\") {\n    activeToastIds.delete(id);\n  }\n});\n\nfunction createToast(\n  message: ReactNode,\n  options: ToastOptions | undefined,\n  type: ToastType,\n): string {\n  const opts = options ?? {};\n\n  const id = toastManager.add({\n    id: opts.id,\n    title: message,\n    description: opts.description,\n    type,\n    timeout: opts.duration,\n    onClose: opts.onDismiss,\n    onRemove: opts.onAutoClose,\n    data: {\n      type,\n      icon: opts.icon,\n      action: opts.action,\n      cancel: opts.cancel,\n      dismissible: opts.dismissible,\n      closeButton: opts.closeButton,\n      richColors: opts.richColors,\n      hideCopyButton: opts.hideCopyButton,\n      expandableContent: opts.expandableContent,\n      expandableLabels: opts.expandableLabels,\n      expandableDescriptionTrigger: opts.expandableDescriptionTrigger,\n      actionLayout: opts.actionLayout,\n      actionVariant: opts.actionVariant,\n    },\n  });\n\n  activeToastIds.add(id);\n  return id;\n}\n\nfunction mapPromiseState<T>(state: PromiseState<T>, type: ToastType): MappedPromiseState<T> {\n  if (typeof state === \"string\") {\n    return state;\n  }\n\n  if (typeof state === \"function\") {\n    return (value: T) => {\n      const result = state(value);\n\n      if (typeof result === \"string\") {\n        return result;\n      }\n\n      return toPromiseToastStateData(result, type);\n    };\n  }\n\n  return toPromiseToastStateData(state, type);\n}\n\nfunction mapStaticPromiseState(\n  state: string | PromiseStateOption,\n  type: ToastType,\n): string | PromiseToastStateData {\n  if (typeof state === \"string\") {\n    return state;\n  }\n\n  return toPromiseToastStateData(state, type);\n}\n\nfunction toPromiseToastStateData(\n  state: PromiseStateOption,\n  type: ToastType,\n): PromiseToastStateData {\n  return {\n    title: state.title,\n    description: state.description,\n    type,\n    data: { type },\n  };\n}\n\nfunction toast(message: ReactNode, options?: ToastOptions): string {\n  return createToast(message, options, \"default\");\n}\n\ntoast.success = (message: ReactNode, options?: ToastOptions): string =>\n  createToast(message, options, \"success\");\n\ntoast.error = (message: ReactNode, options?: ToastOptions): string =>\n  createToast(message, options, \"error\");\n\ntoast.warning = (message: ReactNode, options?: ToastOptions): string =>\n  createToast(message, options, \"warning\");\n\ntoast.info = (message: ReactNode, options?: ToastOptions): string =>\n  createToast(message, options, \"info\");\n\ntoast.loading = (message: ReactNode, options?: ToastOptions): string =>\n  createToast(message, options, \"loading\");\n\ntoast.promise = <T,>(promise: Promise<T>, options: PromiseToastOptions<T>): Promise<T> => {\n  return toastManager.promise<T, ToastData>(promise, {\n    loading: mapStaticPromiseState(options.loading, \"loading\"),\n    success: mapPromiseState(options.success, \"success\"),\n    error: mapPromiseState(options.error, \"error\"),\n  });\n};\n\ntoast.dismiss = (id?: string): void => {\n  if (id) {\n    toastManager.close(id);\n    activeToastIds.delete(id);\n    return;\n  }\n\n  for (const toastId of activeToastIds) {\n    toastManager.close(toastId);\n  }\n  activeToastIds.clear();\n};\n\nexport { toast };\n\nconst defaultIcons: Partial<Record<ToastType, ReactNode>> = {\n  success: <IconCircleCheck />,\n  error: <IconCircleX />,\n  warning: <IconAlertTriangle />,\n  info: <IconInfoCircle />,\n  loading: <Spinner />,\n};\n\nconst richColorStyles: Partial<Record<ToastType, string>> = {\n  success:\n    \"bg-emerald-50 border-emerald-200 text-emerald-900 dark:bg-emerald-950 dark:border-emerald-800 dark:text-emerald-100\",\n  error:\n    \"bg-red-50 border-red-200 text-red-900 dark:bg-red-950 dark:border-red-800 dark:text-red-100\",\n  warning:\n    \"bg-amber-50 border-amber-200 text-amber-900 dark:bg-amber-950 dark:border-amber-800 dark:text-amber-100\",\n  info: \"bg-blue-50 border-blue-200 text-blue-900 dark:bg-blue-950 dark:border-blue-800 dark:text-blue-100\",\n};\n\nconst iconColorStyles: Partial<Record<ToastType, string>> = {\n  success: \"text-emerald-500\",\n  error: \"text-red-500\",\n  warning: \"text-amber-500\",\n  info: \"text-blue-500\",\n  loading: \"text-muted-foreground\",\n};\n\ntype ToastRootRenderState = {\n  expanded: boolean;\n  limited: boolean;\n  swipeDirection: \"up\" | \"down\" | \"left\" | \"right\" | undefined;\n  swiping: boolean;\n  transitionStatus: \"starting\" | \"ending\" | \"idle\" | undefined;\n};\n\ntype ToastViewportStyle = CSSProperties &\n  Record<\"--toast-offset\" | \"--toast-gap\" | \"--toast-peek\", string>;\n\nconst toastRootBaseClassName =\n  \"group absolute w-full overflow-visible rounded-lg border text-popover-foreground select-none shadow-lg/5 z-[calc(1000-var(--toast-index))] [--height:var(--toast-frontmost-height,var(--toast-height))] [--scale:calc(max(0,1-(var(--toast-index)*0.05)))] [--shrink:calc(1-var(--scale))] after:absolute after:left-0 after:h-[calc(var(--toast-gap)+1px)] after:w-full after:content-['']\";\n\nconst toastRootTransitionClassName =\n  \"[transition:transform_0.5s_cubic-bezier(0.22,1,0.36,1),opacity_0.5s,height_0.15s]\";\n\nconst bottomToastRootClassName =\n  \"bottom-0 left-0 origin-bottom [--offset-y:calc(var(--toast-offset-y)*-1+var(--toast-index)*var(--toast-gap)*-1+var(--toast-swipe-movement-y,0px))] after:top-full\";\n\nconst topToastRootClassName =\n  \"top-0 left-0 origin-top [--offset-y:calc(var(--toast-offset-y)+var(--toast-index)*var(--toast-gap)+var(--toast-swipe-movement-y,0px))] after:bottom-full\";\n\nconst bottomToastRestingClassName =\n  \"h-(--height) [transform:translateX(var(--toast-swipe-movement-x,0px))_translateY(calc(var(--toast-swipe-movement-y,0px)-var(--toast-index)*var(--toast-peek)-var(--shrink)*var(--height)))_scale(var(--scale))]\";\n\nconst topToastRestingClassName =\n  \"h-(--height) [transform:translateX(var(--toast-swipe-movement-x,0px))_translateY(calc(var(--toast-swipe-movement-y,0px)+var(--toast-index)*var(--toast-peek)+var(--shrink)*var(--height)))_scale(var(--scale))]\";\n\nconst toastExpandedClassName =\n  \"h-(--toast-height) [transform:translateX(var(--toast-swipe-movement-x,0px))_translateY(var(--offset-y))]\";\n\nconst toastSwipeExitClassNames = {\n  down: \"[transform:translateY(calc(var(--toast-swipe-movement-y,0px)+150%))]\",\n  left: \"[transform:translateX(calc(var(--toast-swipe-movement-x,0px)-150%))_translateY(var(--offset-y,0px))]\",\n  right:\n    \"[transform:translateX(calc(var(--toast-swipe-movement-x,0px)+150%))_translateY(var(--offset-y,0px))]\",\n  up: \"[transform:translateY(calc(var(--toast-swipe-movement-y,0px)-150%))]\",\n} as const;\n\nfunction getToastType(value: unknown): ToastType {\n  switch (value) {\n    case \"success\":\n    case \"error\":\n    case \"warning\":\n    case \"info\":\n    case \"loading\":\n      return value;\n    default:\n      return \"default\";\n  }\n}\n\nfunction getToastViewportClassName(position: ToastPosition) {\n  return cn(\n    \"fixed z-[9999] box-border flex w-[min(26rem,calc(100vw-2rem))] max-w-[calc(100vw-2rem)] outline-none\",\n    position.startsWith(\"top\") ? \"top-(--toast-offset)\" : \"bottom-(--toast-offset)\",\n    position.endsWith(\"left\") && \"left-(--toast-offset)\",\n    position.endsWith(\"right\") && \"right-(--toast-offset)\",\n    position.endsWith(\"center\") && \"left-1/2 -translate-x-1/2\",\n  );\n}\n\nfunction getToastRootClassName({\n  colorStyles,\n  position,\n  state,\n}: {\n  colorStyles: string | undefined;\n  position: ToastPosition;\n  state: ToastRootRenderState;\n}) {\n  const isTop = position.startsWith(\"top\");\n  const isEnding = state.transitionStatus === \"ending\";\n  const isStarting = state.transitionStatus === \"starting\";\n  const swipeExitClassName =\n    isEnding && state.swipeDirection ? toastSwipeExitClassNames[state.swipeDirection] : undefined;\n  const shouldUseVerticalExit =\n    isStarting || (isEnding && !state.limited && state.swipeDirection === undefined);\n  const verticalExitClassName = isTop\n    ? \"[transform:translateY(-150%)]\"\n    : \"[transform:translateY(150%)]\";\n  const restingClassName = state.expanded\n    ? toastExpandedClassName\n    : isTop\n      ? topToastRestingClassName\n      : bottomToastRestingClassName;\n\n  return cn(\n    toastRootBaseClassName,\n    isTop ? topToastRootClassName : bottomToastRootClassName,\n    state.swiping ? \"[transition:none]\" : toastRootTransitionClassName,\n    shouldUseVerticalExit ? verticalExitClassName : (swipeExitClassName ?? restingClassName),\n    (isEnding || state.limited) && \"opacity-0\",\n    colorStyles ?? \"border-border bg-popover\",\n  );\n}\n\nfunction CopyErrorButton({ className, text }: { className?: string; text: string }) {\n  const resetTimeoutRef = useRef<number | undefined>(undefined);\n  const [copied, setCopied] = useState(false);\n\n  useEffect(() => {\n    return () => window.clearTimeout(resetTimeoutRef.current);\n  }, []);\n\n  async function handleClick() {\n    try {\n      await copyTextToClipboard(text);\n      window.clearTimeout(resetTimeoutRef.current);\n      setCopied(true);\n      resetTimeoutRef.current = window.setTimeout(() => setCopied(false), 2000);\n    } catch {\n      setCopied(false);\n    }\n  }\n\n  return (\n    <button\n      aria-label={copied ? \"Copied error details\" : \"Copy error details\"}\n      className={cn(\n        buttonVariants({ size: \"icon-xs\", variant: \"ghost\" }),\n        \"text-muted-foreground/80 hover:text-foreground\",\n        copied && \"text-emerald-600 dark:text-emerald-400\",\n        className,\n      )}\n      onClick={() => {\n        void handleClick();\n      }}\n      title={copied ? \"Copied\" : \"Copy error\"}\n      type=\"button\"\n    >\n      {copied ? <IconCheck className=\"size-3\" /> : <IconCopy className=\"size-3\" />}\n    </button>\n  );\n}\n\nfunction ToastDescription({\n  copyText,\n  toastData,\n}: {\n  copyText?: string | null;\n  toastData: ToastData | undefined;\n}) {\n  const [open, setOpen] = useState(false);\n  const descriptionTrigger = toastData?.expandableDescriptionTrigger ?? false;\n  const expandableContent = toastData?.expandableContent;\n  const labels = toastData?.expandableLabels;\n  const expandLabel = labels?.expand ?? \"Show details\";\n  const collapseLabel = labels?.collapse ?? \"Hide details\";\n  const toggleLabel = open ? collapseLabel : expandLabel;\n  const toggleTitle = typeof toggleLabel === \"string\" ? toggleLabel : undefined;\n  const descriptionClassName =\n    \"min-w-0 text-sm leading-tight wrap-break-word text-muted-foreground/80\";\n\n  function renderDescription() {\n    const description = <ToastPrimitive.Description className={descriptionClassName} />;\n\n    if (!copyText) {\n      return description;\n    }\n\n    return (\n      <div className=\"flex min-w-0 items-start gap-2\">\n        <div className=\"min-w-0 flex-1\">{description}</div>\n        <CopyErrorButton className=\"-mt-1 shrink-0\" text={copyText} />\n      </div>\n    );\n  }\n\n  if (!expandableContent) {\n    return renderDescription();\n  }\n\n  if (descriptionTrigger) {\n    const trigger = (\n      <button\n        aria-expanded={open}\n        className=\"group flex min-w-0 flex-1 items-start gap-1.5 rounded-md py-0.5 text-left transition-colors hover:bg-muted/40\"\n        onClick={() => setOpen((current) => !current)}\n        title={toggleTitle}\n        type=\"button\"\n      >\n        <div className=\"min-w-0 flex-1\">\n          <ToastPrimitive.Description\n            className={cn(\n              descriptionClassName,\n              \"decoration-muted-foreground/60 underline-offset-2 group-hover:underline\",\n            )}\n          />\n        </div>\n        {open ? (\n          <IconChevronUp className=\"mt-0.5 size-3.5 shrink-0 text-muted-foreground\" />\n        ) : (\n          <IconChevronDown className=\"mt-0.5 size-3.5 shrink-0 text-muted-foreground\" />\n        )}\n      </button>\n    );\n\n    return (\n      <>\n        {copyText ? (\n          <div className=\"flex min-w-0 items-start gap-2\">\n            {trigger}\n            <CopyErrorButton className=\"-mt-0.5 shrink-0\" text={copyText} />\n          </div>\n        ) : (\n          trigger\n        )}\n        {open ? (\n          <div className=\"mt-2 max-h-40 min-h-0 overflow-y-auto overscroll-contain pr-0.5 text-sm text-muted-foreground\">\n            {expandableContent}\n          </div>\n        ) : null}\n      </>\n    );\n  }\n\n  return (\n    <>\n      {renderDescription()}\n      <button\n        aria-expanded={open}\n        className=\"mt-1 inline-flex items-center gap-1 rounded-md py-0.5 text-left text-xs font-medium text-muted-foreground transition-colors hover:text-foreground\"\n        onClick={() => setOpen((current) => !current)}\n        type=\"button\"\n      >\n        {open ? <IconChevronUp className=\"size-3.5\" /> : <IconChevronDown className=\"size-3.5\" />}\n        {open ? collapseLabel : expandLabel}\n      </button>\n      {open ? (\n        <div className=\"mt-2 max-h-40 min-h-0 overflow-y-auto overscroll-contain pr-0.5 text-sm text-muted-foreground\">\n          {expandableContent}\n        </div>\n      ) : null}\n    </>\n  );\n}\n\ninterface ToastCardProps {\n  toast: ToastObject<ToastData>;\n  icons?: Partial<Record<ToastType, ReactNode>>;\n  closeButton?: boolean;\n  position: ToastPosition;\n  richColors?: boolean;\n}\n\nfunction ToastCard({\n  toast: t,\n  icons,\n  closeButton = false,\n  position,\n  richColors = false,\n}: ToastCardProps) {\n  const type = getToastType(t.data?.type ?? t.type);\n  const icon = t.data?.icon ?? icons?.[type] ?? defaultIcons[type];\n  const action = t.data?.action;\n  const cancel = t.data?.cancel;\n  const useRichColors = richColors || t.data?.richColors;\n  const colorStyles = useRichColors ? richColorStyles[type] : undefined;\n  const iconColorStyle = !useRichColors ? iconColorStyles[type] : undefined;\n  const showClose = t.data?.dismissible !== false && (closeButton || t.data?.closeButton);\n  const copyErrorText =\n    type === \"error\" && typeof t.description === \"string\" && !t.data?.hideCopyButton\n      ? t.description\n      : null;\n  const stackedActionLayout = (action || cancel) && t.data?.actionLayout === \"stacked-end\";\n  const actionVariant = t.data?.actionVariant ?? \"default\";\n  const hasTrailingControls = Boolean(action) || Boolean(cancel);\n\n  return (\n    <ToastPrimitive.Root\n      toast={t}\n      swipeDirection=\"right\"\n      className={(state) => getToastRootClassName({ colorStyles, position, state })}\n    >\n      {showClose ? (\n        <ToastPrimitive.Close\n          aria-label=\"Close toast\"\n          className={cn(\n            \"absolute top-0 right-0 z-20 translate-x-1/3 -translate-y-1/3\",\n            buttonVariants({ size: \"icon-xs\", variant: \"ghost\" }),\n            \"pointer-events-none rounded-full border border-border/60 bg-background/95 text-muted-foreground opacity-0 shadow-sm backdrop-blur-sm transition-opacity group-hover:pointer-events-auto group-hover:opacity-100 hover:bg-background hover:text-foreground focus-visible:pointer-events-auto focus-visible:opacity-100\",\n          )}\n        >\n          <IconX className=\"size-3\" />\n        </ToastPrimitive.Close>\n      ) : null}\n\n      <ToastPrimitive.Content\n        className={(state) =>\n          cn(\n            \"pointer-events-auto min-h-0 [overflow-x:clip] p-3.5 text-sm transition-opacity duration-[250ms]\",\n            state.behind && !state.expanded && \"pointer-events-none opacity-0\",\n            state.expanded && \"pointer-events-auto opacity-100\",\n            stackedActionLayout ? \"flex flex-col gap-2.5\" : \"flex items-start gap-2.5\",\n          )\n        }\n      >\n        <div className=\"flex min-w-0 flex-1 gap-2\">\n          {icon ? (\n            <div className={cn(\"mt-0.5 shrink-0 [&>svg]:size-4\", iconColorStyle)}>{icon}</div>\n          ) : null}\n\n          <div className=\"min-w-0 flex-1 space-y-1\">\n            <ToastPrimitive.Title className=\"min-w-0 text-sm leading-tight font-medium wrap-break-word\" />\n            <ToastDescription copyText={copyErrorText} toastData={t.data} />\n          </div>\n        </div>\n\n        {hasTrailingControls ? (\n          <div\n            className={cn(\n              \"flex items-center gap-1.5\",\n              stackedActionLayout ? \"w-full justify-end pt-0.5\" : \"mt-0.5 shrink-0\",\n            )}\n          >\n            {action ? (\n              <ToastPrimitive.Action\n                className={cn(buttonVariants({ size: \"xs\", variant: actionVariant }), \"shrink-0\")}\n                onClick={action.onClick}\n              >\n                {action.label}\n              </ToastPrimitive.Action>\n            ) : null}\n            {cancel ? (\n              <ToastPrimitive.Close\n                className={cn(buttonVariants({ size: \"xs\", variant: \"ghost\" }), \"shrink-0\")}\n              >\n                {cancel.label}\n              </ToastPrimitive.Close>\n            ) : null}\n          </div>\n        ) : null}\n      </ToastPrimitive.Content>\n    </ToastPrimitive.Root>\n  );\n}\n\nfunction ToastList({\n  icons,\n  closeButton,\n  position,\n  richColors,\n}: {\n  icons: Partial<Record<ToastType, ReactNode>>;\n  closeButton: boolean;\n  position: ToastPosition;\n  richColors: boolean;\n}) {\n  const { toasts } = ToastPrimitive.useToastManager<ToastData>();\n\n  return toasts.map((t) => (\n    <ToastCard\n      key={t.id}\n      toast={t}\n      icons={icons}\n      closeButton={closeButton}\n      position={position}\n      richColors={richColors}\n    />\n  ));\n}\n\nexport interface ToasterProps {\n  /**\n   * Where toasts appear on screen.\n   * @default \"bottom-right\"\n   */\n  position?: ToastPosition;\n  /**\n   * Maximum number of visible toasts before older ones are removed.\n   * @default 3\n   */\n  visibleToasts?: number;\n  /**\n   * Default auto-dismiss duration in milliseconds.\n   * @default 4000\n   */\n  duration?: number;\n  /**\n   * Show a close button on every toast.\n   * @default false\n   */\n  closeButton?: boolean;\n  /**\n   * Use colorful backgrounds for typed toasts (success, error, etc.).\n   * @default false\n   */\n  richColors?: boolean;\n  /**\n   * Gap between toasts in pixels.\n   * @default 14\n   */\n  gap?: number;\n  /**\n   * Distance from viewport edges in pixels.\n   * @default 32\n   */\n  offset?: number;\n  /**\n   * Override default icons per toast type.\n   */\n  icons?: Partial<Record<ToastType, ReactNode>>;\n}\n\nfunction Toaster({\n  position = \"bottom-right\",\n  visibleToasts = 3,\n  duration = 4000,\n  closeButton = false,\n  richColors = false,\n  gap = 14,\n  offset = 32,\n  icons = {},\n}: ToasterProps) {\n  const viewportStyle: ToastViewportStyle = {\n    \"--toast-offset\": `${offset}px`,\n    \"--toast-gap\": `${gap}px`,\n    \"--toast-peek\": \"10px\",\n  };\n\n  return (\n    <ToastPrimitive.Provider toastManager={toastManager} timeout={duration} limit={visibleToasts}>\n      <ToastPrimitive.Portal>\n        <ToastPrimitive.Viewport\n          className={getToastViewportClassName(position)}\n          style={viewportStyle}\n        >\n          <ToastList\n            icons={icons}\n            closeButton={closeButton}\n            position={position}\n            richColors={richColors}\n          />\n        </ToastPrimitive.Viewport>\n      </ToastPrimitive.Portal>\n    </ToastPrimitive.Provider>\n  );\n}\n\nasync function copyTextToClipboard(value: string) {\n  if (navigator.clipboard?.writeText) {\n    await navigator.clipboard.writeText(value);\n    return;\n  }\n\n  const textArea = document.createElement(\"textarea\");\n\n  textArea.value = value;\n  textArea.setAttribute(\"readonly\", \"\");\n  textArea.style.position = \"fixed\";\n  textArea.style.inset = \"0 auto auto -9999px\";\n  document.body.append(textArea);\n  textArea.select();\n\n  const copied = document.execCommand(\"copy\");\n\n  textArea.remove();\n\n  if (!copied) {\n    throw new Error(\"Copy command was rejected.\");\n  }\n}\n\nexport { Toaster };","target":"@ui/toast.tsx"}],"type":"registry:ui","dependencies":["@base-ui/react","@tabler/icons-react"],"registryDependencies":["button","spinner"]}