{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "toast",
  "type": "registry:ui",
  "title": "Toast",
  "description": "Notifications that print as receipt slips and tear away.",
  "categories": [
    "core"
  ],
  "files": [
    {
      "path": "ui/toast.tsx",
      "type": "registry:ui",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\ntype Toast = { id: number; title: string; description?: string; tone: \"default\" | \"danger\"; leaving?: boolean };\ntype Options = { description?: string; tone?: Toast[\"tone\"]; duration?: number };\n\nlet toasts: Toast[] = [];\nlet nextId = 1;\nconst listeners = new Set<() => void>();\nconst emit = () => listeners.forEach((l) => l());\n\nfunction dismiss(id: number) {\n  toasts = toasts.map((t) => (t.id === id ? { ...t, leaving: true } : t));\n  emit();\n  setTimeout(() => {\n    toasts = toasts.filter((t) => t.id !== id);\n    emit();\n  }, 350);\n}\n\n/** Print a receipt slip. Returns its id. */\nexport function toast(title: string, { description, tone = \"default\", duration = 4000 }: Options = {}) {\n  const id = nextId++;\n  toasts = [...toasts, { id, title, description, tone }].slice(-4);\n  emit();\n  if (duration !== Infinity) setTimeout(() => dismiss(id), duration);\n  return id;\n}\ntoast.dismiss = dismiss;\n\nconst subscribe = (l: () => void) => {\n  listeners.add(l);\n  return () => listeners.delete(l);\n};\nconst EMPTY: Toast[] = [];\n\nconst EDGE = \"conic-gradient(from -45deg at bottom, #0000, #000 1deg 89deg, #0000 90deg) 50% / 10px 100%\";\n\n/** Render once, near the root of your app. */\nexport function Toaster({ position = \"bottom-right\" }: { position?: \"bottom-right\" | \"top-right\" | \"bottom-center\" }) {\n  const list = React.useSyncExternalStore(\n    subscribe,\n    () => toasts,\n    () => EMPTY,\n  );\n  const pos =\n    position === \"top-right\"\n      ? \"top-4 right-4 items-end\"\n      : position === \"bottom-center\"\n        ? \"bottom-4 left-1/2 -translate-x-1/2 items-center\"\n        : \"bottom-4 right-4 items-end\";\n\n  return (\n    <div aria-live=\"polite\" className={`pointer-events-none fixed z-[100] flex w-[300px] max-w-[calc(100vw-2rem)] flex-col gap-3 ${pos}`}>\n      {list.map((t) => (\n        <div\n          key={t.id}\n          role=\"status\"\n          data-paper-motion\n          className=\"pointer-events-auto w-full\"\n          style={{\n            filter: \"drop-shadow(0 6px 10px rgb(0 0 0 / .2))\",\n            animation: t.leaving ? \"paper-tear-away .35s ease-in both\" : \"paper-print .45s steps(8) both\",\n          }}\n        >\n          <div\n            className=\"paper relative bg-(--paper-raised) px-4 pb-5 pt-3.5 text-(--ink)\"\n            style={{ mask: EDGE, WebkitMask: EDGE, fontFamily: \"var(--font-receipt, var(--font-type, ui-monospace, monospace))\" }}\n          >\n            <div className=\"flex items-start justify-between gap-3\">\n              <div className=\"min-w-0\">\n                <div\n                  className={`text-[12.5px] font-bold uppercase tracking-[0.12em] ${t.tone === \"danger\" ? \"text-(--danger)\" : \"\"}`}\n                >\n                  {t.title}\n                </div>\n                {t.description && <div className=\"mt-1 text-[12px] leading-snug text-(--ink-muted)\">{t.description}</div>}\n              </div>\n              <button\n                type=\"button\"\n                onClick={() => dismiss(t.id)}\n                aria-label=\"Dismiss\"\n                className=\"-mr-1 -mt-0.5 cursor-pointer px-1 text-[15px] leading-none text-(--ink-faint) hover:text-(--ink)\"\n              >\n                ×\n              </button>\n            </div>\n            <div className=\"mt-2.5 border-t border-dashed border-(--ink-faint)\" />\n          </div>\n        </div>\n      ))}\n    </div>\n  );\n}\n"
    }
  ]
}