{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "async-scope",
  "type": "registry:component",
  "description": "A coordination layer for async UI — decouple what fires an action from what reacts to it.",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [
    "utils",
    "button",
    "https://kaui-shadcn-registry.vercel.app/r/use-async.json"
  ],
  "files": [
    {
      "type": "registry:component",
      "target": "components/ui/async-scope.tsx",
      "path": "src/registry/base/async-scope/components/async-scope.tsx",
      "content": "import {\n  createContext,\n  useContext,\n  type ComponentProps,\n  type PropsWithChildren,\n  type ReactNode,\n} from \"react\";\nimport {\n  useAsync,\n  type UseAsyncOptions,\n  type UseAsyncReturn,\n} from \"@/hooks/use-async\";\nimport { Button } from \"@/components/ui/button\";\nimport { Loader2 } from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\n\nconst AsyncScopeContext = createContext<UseAsyncReturn<\n  unknown,\n  unknown,\n  []\n> | null>(null);\n\n/**\n * Read the async state from the nearest `AsyncScope`.\n *\n * ⚠️ The `TData` / `TError` generics are an **assertion, not a guarantee**.\n * Context can't infer them across the provider boundary, so you must pass the\n * same types the enclosing `<AsyncScope action={...}>` actually produces.\n * Mismatched generics compile cleanly but are wrong at runtime.\n */\nexport function useAsyncScope<\n  TData = unknown,\n  TError = unknown,\n>(): UseAsyncReturn<TData, TError, []> {\n  const ctx = useContext(AsyncScopeContext);\n  if (!ctx) throw new Error(\"useAsyncScope must be used within AsyncScope\");\n  return ctx as UseAsyncReturn<TData, TError, []>;\n}\n\nexport type AsyncScopeProps<\n  TData = unknown,\n  TError = unknown,\n> = PropsWithChildren & UseAsyncOptions<TData, TError, []>;\n\n/**\n * A coordination layer of async UI , decouples what fires an action from what reacts to it\n *\n * required : `action`\n */\nexport function AsyncScope<TData = unknown, TError = unknown>({\n  children,\n  action,\n  onSuccess,\n  onError,\n  onSettled,\n}: AsyncScopeProps<TData, TError>) {\n  const state = useAsync<TData, TError, []>({\n    action,\n    onSuccess,\n    onError,\n    onSettled,\n  });\n\n  return (\n    <AsyncScopeContext.Provider\n      value={state as UseAsyncReturn<unknown, unknown, []>}\n    >\n      {children}\n    </AsyncScopeContext.Provider>\n  );\n}\n\nexport type AsyncTriggerProps = Omit<ComponentProps<typeof Button>, \"onClick\">;\n\nexport function AsyncTrigger({\n  children,\n  disabled,\n  ...props\n}: AsyncTriggerProps) {\n  const { isLoading, execute } = useAsyncScope();\n\n  return (\n    <Button\n      {...props}\n      disabled={disabled || isLoading}\n      onClick={() => execute()}\n    >\n      {children}\n    </Button>\n  );\n}\n\nexport type AsyncContentProps = PropsWithChildren & {\n  loadingFallback?: ReactNode;\n  className?: string;\n};\n\nexport function AsyncContent({\n  children,\n  loadingFallback,\n  className,\n}: AsyncContentProps) {\n  const { isLoading } = useAsyncScope();\n\n  return (\n    <div className=\"grid grid-cols-1 items-center justify-items-center\">\n      <div\n        className={cn(\n          \"col-start-1 col-end-2 row-start-1 row-end-2 w-full\",\n          isLoading ? \"invisible\" : \"visible\",\n          className,\n        )}\n      >\n        {children}\n      </div>\n      <div\n        className={cn(\n          \"col-start-1 col-end-2 row-start-1 row-end-2\",\n          isLoading ? \"visible\" : \"invisible\",\n        )}\n      >\n        {loadingFallback ?? <Loader2 className=\"size-6 animate-spin\" />}\n      </div>\n    </div>\n  );\n}\n"
    }
  ]
}