{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "toast",
  "title": "Toast",
  "description": "A temporary notification that stacks in a corner of the screen or anchors to an element, built on Base UI, with success/error/warning/info/loading and X-style variants.",
  "dependencies": [
    "@base-ui/react",
    "lucide-react"
  ],
  "registryDependencies": [
    "utils",
    "https://ui.persian-labs.ir/r/button.json"
  ],
  "files": [
    {
      "path": "registry/base/ui/toast.tsx",
      "content": "\"use client\"\n\nimport { Toast as ToastPrimitive } from \"@base-ui/react/toast\"\nimport {\n  CircleAlertIcon,\n  CircleCheckIcon,\n  InfoIcon,\n  Loader2Icon,\n  TriangleAlertIcon,\n  XIcon,\n} from \"lucide-react\"\nimport * as React from \"react\"\n\nimport { Button, buttonVariants } from \"@/components/ui/button\"\nimport { cn } from \"@/lib/utils\"\n\nconst TOAST_ICONS = {\n  success: CircleCheckIcon,\n  info: InfoIcon,\n  warning: TriangleAlertIcon,\n  error: CircleAlertIcon,\n  loading: Loader2Icon,\n} as const\n\ntype ToastStatus = keyof typeof TOAST_ICONS\n\ntype SwipeDirection = \"up\" | \"down\" | \"left\" | \"right\"\n\ntype ToastVariant = \"default\" | \"x\"\n\ninterface ToastData {\n  /** Visual style of the toast. Set per-toast via `toastManager.add({ data: { variant: \"x\" } })`. */\n  variant?: ToastVariant\n  /** Avatar/icon rendered in the `x` variant instead of the status icon. */\n  avatar?: React.ReactNode\n  /**\n   * Forces this toast's own content direction, overriding the ambient\n   * direction the provider otherwise measures automatically. Only affects\n   * this toast's Root (text/logical spacing) — the stack's corner position\n   * still follows the provider's ambient/measured direction.\n   */\n  dir?: \"ltr\" | \"rtl\"\n  rootProps?: Omit<\n    React.ComponentProps<typeof ToastPrimitive.Root>,\n    \"children\" | \"className\" | \"swipeDirection\" | \"toast\"\n  >\n}\n\ntype ManagedToast = ReturnType<\n  typeof ToastPrimitive.useToastManager\n>[\"toasts\"][number]\n\n/**\n * Logical viewport corners — `start`/`end` follow reading direction instead\n * of a hardcoded screen side, so `bottom-end` lands bottom-right in LTR and\n * bottom-left in RTL. Resolved to a physical position at render time by\n * `ToastProvider`/`AnchoredToastProvider`, which track the ambient direction\n * (see the dir-tracking note below).\n */\nexport type ToastPosition =\n  | \"top-start\"\n  | \"top-center\"\n  | \"top-end\"\n  | \"bottom-start\"\n  | \"bottom-center\"\n  | \"bottom-end\"\n\ntype PhysicalToastPosition =\n  | \"top-left\"\n  | \"top-center\"\n  | \"top-right\"\n  | \"bottom-left\"\n  | \"bottom-center\"\n  | \"bottom-right\"\n\nfunction resolveToastPosition(\n  position: ToastPosition,\n  dir: \"ltr\" | \"rtl\"\n): PhysicalToastPosition {\n  const [vertical, horizontal] = position.split(\"-\") as [\n    \"top\" | \"bottom\",\n    \"start\" | \"center\" | \"end\",\n  ]\n\n  if (horizontal === \"center\") {\n    return `${vertical}-center`\n  }\n\n  const isStart = horizontal === \"start\"\n  const isPhysicalRight =\n    (isStart && dir === \"rtl\") || (!isStart && dir === \"ltr\")\n\n  return `${vertical}-${isPhysicalRight ? \"right\" : \"left\"}`\n}\n\nfunction getSwipeDirection(position: PhysicalToastPosition): SwipeDirection[] {\n  const vertical: SwipeDirection = position.startsWith(\"top\") ? \"up\" : \"down\"\n\n  if (position.includes(\"center\")) return [vertical]\n  if (position.includes(\"left\")) return [\"left\", vertical]\n  return [\"right\", vertical]\n}\n\nfunction statusIconColor(type?: string) {\n  switch (type) {\n    case \"success\":\n      return \"text-success\"\n    case \"info\":\n      return \"text-info\"\n    case \"warning\":\n      return \"text-warning\"\n    case \"error\":\n      return \"text-destructive\"\n    default:\n      return undefined\n  }\n}\n\nfunction toastAppearance(variant: ToastVariant) {\n  switch (variant) {\n    case \"x\":\n      return \"rounded-full border-0 bg-neutral-900 text-white shadow-[var(--shadow-elevation-lg)] before:hidden dark:bg-neutral-50 dark:text-neutral-900\"\n    default:\n      return \"rounded-2xl border bg-[color-mix(in_srgb,var(--popover),var(--color-black)_calc(1%*max(0,var(--toast-index,0))))] text-popover-foreground not-dark:bg-clip-padding shadow-[var(--shadow-elevation-md)] before:hidden data-expanded:bg-popover dark:bg-[color-mix(in_srgb,var(--popover),var(--color-black)_calc(6%*max(0,var(--toast-index,0))))] dark:data-expanded:bg-popover\"\n  }\n}\n\nconst TOAST_ROOT_BASE = cn(\n  \"absolute z-[calc(9999-var(--toast-index))] h-(--toast-calc-height) w-full origin-[50%_0%] outline-none select-none [transition:transform_.5s_cubic-bezier(.22,1,.36,1),opacity_.5s,height_.15s,background-color_.5s]\",\n  \"before:pointer-events-none before:absolute before:inset-0\",\n  // Horizontal positioning\n  \"data-[position*=right]:right-0 data-[position*=right]:left-auto\",\n  \"data-[position*=left]:right-auto data-[position*=left]:left-0\",\n  \"data-[position*=center]:right-0 data-[position*=center]:left-0\",\n  // Vertical positioning + stack origin\n  \"data-[position*=top]:top-0 data-[position*=top]:bottom-auto data-[position*=top]:origin-[50%_calc(50%-50%*min(var(--toast-index,0),1))]\",\n  \"data-[position*=bottom]:top-auto data-[position*=bottom]:bottom-0 data-[position*=bottom]:origin-[50%_calc(50%+50%*min(var(--toast-index,0),1))]\",\n  // Gap fill so hovering the space between stacked toasts still counts as \"hovering\"\n  \"after:pointer-events-none after:absolute after:inset-x-0 after:h-[calc(var(--toast-gap)+1px)]\",\n  \"data-[position*=bottom]:after:bottom-full data-[position*=top]:after:top-full\",\n  // Stack variables\n  \"[--toast-calc-height:var(--toast-frontmost-height,var(--toast-height))] [--toast-gap:--spacing(3)] [--toast-peek:--spacing(3)] [--toast-scale:calc(max(0,1-(var(--toast-index)*.1)))] [--toast-shrink:calc(1-var(--toast-scale))]\",\n  \"data-[position*=top]:[--toast-calc-offset-y:calc(var(--toast-offset-y)+var(--toast-index)*var(--toast-gap)+var(--toast-swipe-movement-y))]\",\n  \"data-[position*=bottom]:[--toast-calc-offset-y:calc(var(--toast-offset-y)*-1+var(--toast-index)*var(--toast-gap)*-1+var(--toast-swipe-movement-y))]\",\n  // Default (collapsed stack) transform\n  \"data-[position*=top]:transform-[translateX(var(--toast-swipe-movement-x))_translateY(calc(var(--toast-swipe-movement-y)+(var(--toast-index)*var(--toast-peek))+(var(--toast-shrink)*var(--toast-calc-height))))_scale(var(--toast-scale))]\",\n  \"data-[position*=bottom]:transform-[translateX(var(--toast-swipe-movement-x))_translateY(calc(var(--toast-swipe-movement-y)-(var(--toast-index)*var(--toast-peek))-(var(--toast-shrink)*var(--toast-calc-height))))_scale(var(--toast-scale))]\",\n  // Limited / expanded\n  \"data-expanded:h-(--toast-height) data-limited:opacity-0\",\n  \"data-position:data-expanded:transform-[translateX(var(--toast-swipe-movement-x))_translateY(var(--toast-calc-offset-y))]\",\n  // Starting / ending animations\n  \"data-[position*=top]:data-starting-style:transform-[translateY(calc(-100%-var(--toast-inset)))]\",\n  \"data-[position*=bottom]:data-starting-style:transform-[translateY(calc(100%+var(--toast-inset)))]\",\n  \"data-ending-style:opacity-0\",\n  \"data-[position*=top]:data-ending-style:not-data-limited:not-data-swipe-direction:transform-[translateY(calc(-100%-var(--toast-inset)))]\",\n  \"data-[position*=bottom]:data-ending-style:not-data-limited:not-data-swipe-direction:transform-[translateY(calc(100%+var(--toast-inset)))]\",\n  \"data-ending-style:data-[swipe-direction=left]:transform-[translateX(calc(var(--toast-swipe-movement-x)-100%-var(--toast-inset)))_translateY(var(--toast-calc-offset-y))]\",\n  \"data-ending-style:data-[swipe-direction=right]:transform-[translateX(calc(var(--toast-swipe-movement-x)+100%+var(--toast-inset)))_translateY(var(--toast-calc-offset-y))]\",\n  \"data-ending-style:data-[swipe-direction=up]:transform-[translateY(calc(var(--toast-swipe-movement-y)-100%-var(--toast-inset)))]\",\n  \"data-ending-style:data-[swipe-direction=down]:transform-[translateY(calc(var(--toast-swipe-movement-y)+100%+var(--toast-inset)))]\",\n  \"data-expanded:data-ending-style:data-[swipe-direction=left]:transform-[translateX(calc(var(--toast-swipe-movement-x)-100%-var(--toast-inset)))_translateY(var(--toast-calc-offset-y))]\",\n  \"data-expanded:data-ending-style:data-[swipe-direction=right]:transform-[translateX(calc(var(--toast-swipe-movement-x)+100%+var(--toast-inset)))_translateY(var(--toast-calc-offset-y))]\",\n  \"data-expanded:data-ending-style:data-[swipe-direction=up]:transform-[translateY(calc(var(--toast-swipe-movement-y)-100%-var(--toast-inset)))]\",\n  \"data-expanded:data-ending-style:data-[swipe-direction=down]:transform-[translateY(calc(var(--toast-swipe-movement-y)+100%+var(--toast-inset)))]\"\n)\n\nconst ANCHORED_ROOT_BASE =\n  \"relative z-50 max-w-[min(--spacing(72),var(--available-width))] text-start text-balance outline-none transition-[scale,opacity] data-ending-style:scale-98 data-ending-style:opacity-0 data-starting-style:scale-98 data-starting-style:opacity-0\"\n\n/**\n * Both providers render through a Portal, so their content doesn't inherit\n * `dir` from a nearby wrapper (only from document.documentElement). Each\n * provider wraps its (non-portaled) children in a zero-box `<span>` and\n * measures the ambient CSS direction there, then pushes it explicitly onto\n * the portaled Viewport/Positioner so logical spacing and swipe-to-dismiss\n * direction resolve correctly even when the provider itself sits in an\n * otherwise-LTR page.\n */\nfunction useAmbientDir() {\n  const [dir, setDir] = React.useState<\"ltr\" | \"rtl\">(\"ltr\")\n  const anchorRef = React.useRef<HTMLSpanElement>(null)\n\n  React.useEffect(() => {\n    function update() {\n      if (!anchorRef.current) return\n      setDir(\n        getComputedStyle(anchorRef.current).direction === \"rtl\" ? \"rtl\" : \"ltr\"\n      )\n    }\n\n    update()\n\n    const observer = new MutationObserver(update)\n    observer.observe(document.documentElement, {\n      attributes: true,\n      attributeFilter: [\"dir\"],\n      subtree: true,\n    })\n\n    return () => observer.disconnect()\n  }, [])\n\n  return { dir, anchorRef }\n}\n\nfunction ToastBody({\n  toast,\n  variant,\n}: {\n  toast: ManagedToast\n  variant: ToastVariant\n}) {\n  const data = toast.data as ToastData | undefined\n  const Icon = toast.type ? TOAST_ICONS[toast.type as ToastStatus] : undefined\n\n  if (variant === \"x\") {\n    return (\n      <ToastPrimitive.Content\n        data-slot=\"toast-content\"\n        className=\"pointer-events-auto flex items-start gap-2.5 overflow-hidden py-2.5 ps-2.5 pe-3 text-sm transition-opacity duration-250 data-behind:opacity-0 data-expanded:opacity-100\"\n      >\n        <span className=\"flex size-9 shrink-0 items-center justify-center self-center overflow-hidden rounded-full bg-white/10 dark:bg-neutral-900/10 [&>svg]:pointer-events-none [&>svg:not([class*='size-'])]:size-4\">\n          {data?.avatar ??\n            (Icon && (\n              <Icon\n                className={cn(\n                  \"in-data-[type=loading]:animate-spin\",\n                  statusIconColor(toast.type)\n                )}\n              />\n            ))}\n        </span>\n        <div className=\"flex min-w-0 flex-1 flex-col\">\n          <ToastPrimitive.Title\n            data-slot=\"toast-title\"\n            className=\"truncate text-sm font-semibold\"\n          />\n          <ToastPrimitive.Description\n            data-slot=\"toast-description\"\n            className=\"truncate text-xs text-white/60 dark:text-neutral-900/60\"\n          />\n        </div>\n        <ToastPrimitive.Action\n          data-slot=\"toast-action\"\n          render={\n            <button\n              type=\"button\"\n              className={cn(\n                buttonVariants({ size: \"xs\" }),\n                \"shrink-0 bg-white text-neutral-900 hover:bg-white/90 dark:bg-neutral-900 dark:text-white dark:hover:bg-neutral-900/90\"\n              )}\n            />\n          }\n        />\n        <ToastPrimitive.Close\n          aria-label=\"Close toast\"\n          data-slot=\"toast-close\"\n          render={<button type=\"button\" />}\n          className=\"relative shrink-0 self-center rounded-full p-1 text-white/60 after:absolute after:-inset-2 after:content-[''] hover:bg-white/10 hover:text-white dark:text-neutral-900/60 dark:hover:bg-neutral-900/10 dark:hover:text-neutral-900\"\n        >\n          <XIcon className=\"size-3.5\" />\n        </ToastPrimitive.Close>\n      </ToastPrimitive.Content>\n    )\n  }\n\n  return (\n    <ToastPrimitive.Content\n      data-slot=\"toast-content\"\n      className=\"pointer-events-auto flex items-start gap-3 overflow-hidden p-4 text-sm transition-opacity duration-250 data-behind:opacity-0 data-expanded:opacity-100\"\n    >\n      {Icon && (\n        <span\n          data-slot=\"toast-icon\"\n          className=\"mt-0.5 shrink-0 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4\"\n        >\n          <Icon\n            className={cn(\n              \"in-data-[type=loading]:animate-spin\",\n              statusIconColor(toast.type)\n            )}\n          />\n        </span>\n      )}\n      <div className=\"flex min-w-0 flex-1 flex-col gap-1\">\n        <ToastPrimitive.Title\n          data-slot=\"toast-title\"\n          className=\"text-sm font-medium\"\n        />\n        <ToastPrimitive.Description\n          data-slot=\"toast-description\"\n          className=\"text-sm text-muted-foreground\"\n        />\n      </div>\n      <ToastPrimitive.Action\n        data-slot=\"toast-action\"\n        render={<Button variant=\"outline\" size=\"xs\" />}\n        className=\"shrink-0\"\n      />\n      <ToastPrimitive.Close\n        aria-label=\"Close toast\"\n        data-slot=\"toast-close\"\n        render={<Button variant=\"ghost\" size=\"icon-xs\" />}\n        className=\"relative shrink-0 text-muted-foreground after:absolute after:-inset-2 after:content-[''] hover:text-foreground\"\n      >\n        <XIcon />\n      </ToastPrimitive.Close>\n    </ToastPrimitive.Content>\n  )\n}\n\n/**\n * Anchored toasts are tooltip-like confirmations next to the element that\n * triggered them, so they stay deliberately tiny — no action or close\n * button, just an icon/avatar and title/description at a compact size.\n */\nfunction AnchoredToastBody({\n  toast,\n  variant,\n}: {\n  toast: ManagedToast\n  variant: ToastVariant\n}) {\n  const data = toast.data as ToastData | undefined\n  const Icon = toast.type ? TOAST_ICONS[toast.type as ToastStatus] : undefined\n\n  if (variant === \"x\") {\n    return (\n      <ToastPrimitive.Content\n        data-slot=\"toast-content\"\n        className=\"pointer-events-auto flex items-center gap-1.5 overflow-hidden px-2 py-1.5 text-xs transition-opacity duration-250 data-behind:opacity-0 data-expanded:opacity-100\"\n      >\n        <span className=\"flex size-5 shrink-0 items-center justify-center overflow-hidden rounded-full bg-white/10 dark:bg-neutral-900/10 [&>svg]:pointer-events-none [&>svg:not([class*='size-'])]:size-3\">\n          {data?.avatar ??\n            (Icon && (\n              <Icon\n                className={cn(\n                  \"in-data-[type=loading]:animate-spin\",\n                  statusIconColor(toast.type)\n                )}\n              />\n            ))}\n        </span>\n        <div className=\"flex min-w-0 flex-col\">\n          <ToastPrimitive.Title\n            data-slot=\"toast-title\"\n            className=\"truncate text-xs font-semibold\"\n          />\n          <ToastPrimitive.Description\n            data-slot=\"toast-description\"\n            className=\"truncate text-[11px] text-white/60 dark:text-neutral-900/60\"\n          />\n        </div>\n      </ToastPrimitive.Content>\n    )\n  }\n\n  return (\n    <ToastPrimitive.Content\n      data-slot=\"toast-content\"\n      className=\"pointer-events-auto flex items-center gap-1.5 overflow-hidden px-2 py-1.5 text-xs transition-opacity duration-250 data-behind:opacity-0 data-expanded:opacity-100\"\n    >\n      {Icon && (\n        <span\n          data-slot=\"toast-icon\"\n          className=\"shrink-0 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-3.5\"\n        >\n          <Icon\n            className={cn(\n              \"in-data-[type=loading]:animate-spin\",\n              statusIconColor(toast.type)\n            )}\n          />\n        </span>\n      )}\n      <div className=\"flex min-w-0 flex-col\">\n        <ToastPrimitive.Title\n          data-slot=\"toast-title\"\n          className=\"text-xs font-medium\"\n        />\n        <ToastPrimitive.Description\n          data-slot=\"toast-description\"\n          className=\"text-[11px] text-muted-foreground\"\n        />\n      </div>\n    </ToastPrimitive.Content>\n  )\n}\n\nfunction Toasts({\n  position = \"bottom-end\",\n  dir = \"ltr\",\n  portalProps,\n}: {\n  position?: ToastPosition\n  dir?: \"ltr\" | \"rtl\"\n  portalProps?: ToastPrimitive.Portal.Props\n}) {\n  const { toasts } = ToastPrimitive.useToastManager()\n  const physicalPosition = resolveToastPosition(position, dir)\n  const swipeDirection = getSwipeDirection(physicalPosition)\n\n  return (\n    <ToastPrimitive.Portal data-slot=\"toast-portal\" {...portalProps}>\n      <ToastPrimitive.Viewport\n        dir={dir}\n        data-slot=\"toast-viewport\"\n        data-position={physicalPosition}\n        className={cn(\n          \"pointer-events-none fixed z-60 mx-auto flex w-[calc(100%-var(--toast-inset)*2)] max-w-90 [--toast-inset:--spacing(4)] sm:[--toast-inset:--spacing(8)]\",\n          \"data-[position*=top]:top-(--toast-inset)\",\n          \"data-[position*=bottom]:bottom-(--toast-inset)\",\n          \"data-[position*=left]:left-(--toast-inset)\",\n          \"data-[position*=right]:right-(--toast-inset)\",\n          \"data-[position*=center]:left-1/2 data-[position*=center]:-translate-x-1/2\"\n        )}\n      >\n        {toasts.map((toastItem) => {\n          const data = toastItem.data as ToastData | undefined\n          const variant = data?.variant ?? \"default\"\n          const toastDir = data?.dir ?? dir\n\n          return (\n            <ToastPrimitive.Root\n              key={toastItem.id}\n              dir={toastDir}\n              data-slot=\"toast\"\n              data-position={physicalPosition}\n              data-variant={variant}\n              swipeDirection={swipeDirection}\n              toast={toastItem}\n              className={cn(TOAST_ROOT_BASE, toastAppearance(variant))}\n              {...data?.rootProps}\n            >\n              <ToastBody toast={toastItem} variant={variant} />\n            </ToastPrimitive.Root>\n          )\n        })}\n      </ToastPrimitive.Viewport>\n    </ToastPrimitive.Portal>\n  )\n}\n\nfunction AnchoredToasts({\n  dir = \"ltr\",\n  portalProps,\n}: {\n  dir?: \"ltr\" | \"rtl\"\n  portalProps?: ToastPrimitive.Portal.Props\n}) {\n  const { toasts } = ToastPrimitive.useToastManager()\n\n  return (\n    <ToastPrimitive.Portal data-slot=\"toast-portal-anchored\" {...portalProps}>\n      <ToastPrimitive.Viewport\n        dir={dir}\n        data-slot=\"toast-viewport-anchored\"\n        className=\"outline-none\"\n      >\n        {toasts.map((toastItem) => {\n          const data = toastItem.data as ToastData | undefined\n          const variant = data?.variant ?? \"default\"\n          const toastDir = data?.dir ?? dir\n          const positionerProps = toastItem.positionerProps\n\n          if (!positionerProps?.anchor) {\n            return null\n          }\n\n          return (\n            <ToastPrimitive.Positioner\n              key={toastItem.id}\n              data-slot=\"toast-positioner\"\n              dir={dir}\n              side=\"bottom\"\n              sideOffset={8}\n              align=\"center\"\n              className=\"z-50\"\n              toast={toastItem}\n              {...positionerProps}\n            >\n              <ToastPrimitive.Root\n                dir={toastDir}\n                data-slot=\"toast-popup\"\n                data-variant={variant}\n                toast={toastItem}\n                className={cn(ANCHORED_ROOT_BASE, toastAppearance(variant))}\n                {...data?.rootProps}\n              >\n                <ToastBody toast={toastItem} variant={variant} />\n              </ToastPrimitive.Root>\n            </ToastPrimitive.Positioner>\n          )\n        })}\n      </ToastPrimitive.Viewport>\n    </ToastPrimitive.Portal>\n  )\n}\n\nexport const toastManager: ReturnType<\n  typeof ToastPrimitive.createToastManager\n> = ToastPrimitive.createToastManager()\n\nexport const anchoredToastManager: ReturnType<\n  typeof ToastPrimitive.createToastManager\n> = ToastPrimitive.createToastManager()\n\nexport interface ToastProviderProps extends ToastPrimitive.Provider.Props {\n  /** Which corner the toasts stack in. `start`/`end` follow reading direction. @default \"bottom-end\" */\n  position?: ToastPosition\n  portalProps?: ToastPrimitive.Portal.Props\n}\n\nfunction ToastProvider({\n  children,\n  position = \"bottom-end\",\n  portalProps,\n  toastManager: manager = toastManager,\n  ...props\n}: ToastProviderProps) {\n  const { dir, anchorRef } = useAmbientDir()\n\n  return (\n    <ToastPrimitive.Provider toastManager={manager} {...props}>\n      <span\n        ref={anchorRef}\n        data-slot=\"toast-dir-anchor\"\n        style={{ display: \"contents\" }}\n      >\n        {children}\n      </span>\n      <Toasts position={position} dir={dir} portalProps={portalProps} />\n    </ToastPrimitive.Provider>\n  )\n}\n\nexport interface AnchoredToastProviderProps\n  extends ToastPrimitive.Provider.Props {\n  portalProps?: ToastPrimitive.Portal.Props\n}\n\nfunction AnchoredToastProvider({\n  children,\n  portalProps,\n  toastManager: manager = anchoredToastManager,\n  ...props\n}: AnchoredToastProviderProps) {\n  const { dir, anchorRef } = useAmbientDir()\n\n  return (\n    <ToastPrimitive.Provider toastManager={manager} {...props}>\n      <span\n        ref={anchorRef}\n        data-slot=\"toast-dir-anchor\"\n        style={{ display: \"contents\" }}\n      >\n        {children}\n      </span>\n      <AnchoredToasts dir={dir} portalProps={portalProps} />\n    </ToastPrimitive.Provider>\n  )\n}\n\nexport { ToastPrimitive }\nexport { ToastProvider, AnchoredToastProvider }\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}