{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "countdown",
  "type": "registry:ui",
  "title": "Countdown",
  "description": "شمارش معکوس با ارقام فارسی؛ هر ثانیه به‌روز می‌شود و در پایان onComplete را صدا می‌زند",
  "dependencies": [
    "@kashi-ui/persian@^1.8.0"
  ],
  "registryDependencies": [
    "https://kashi.sepantapouya.com/r/cx.json",
    "https://kashi.sepantapouya.com/r/kashi-provider.json"
  ],
  "files": [
    {
      "path": "registry/kashi/Countdown.tsx",
      "type": "registry:ui",
      "target": "components/kashi/Countdown.tsx",
      "content": "\"use client\";\n\nimport { useEffect, useRef, useState } from \"react\";\nimport { toFaDigits } from \"@kashi-ui/persian\";\nimport { cx } from \"./cx\";\nimport { useKashi } from \"./KashiProvider\";\n\nexport interface CountdownProps {\n  /** زمان پایان */\n  to?: Date;\n  /** یا مدت به ثانیه از لحظهٔ نمایش (مثلاً ۱۲۰ برای ارسال دوبارهٔ کد) */\n  seconds?: number;\n  onComplete?: () => void;\n  /** «clock» مثل ۰۲:۳۴، «blocks» کادرهای روز/ساعت/دقیقه/ثانیه @default \"clock\" */\n  variant?: \"clock\" | \"blocks\";\n  /** @default \"fa\" */\n  digits?: \"fa\" | \"latin\";\n  /** نام برای صفحه‌خوان، مثل «زمان باقی‌مانده تا ارسال دوباره» */\n  \"aria-label\"?: string;\n  className?: string;\n  labels?: { days?: string; hours?: string; minutes?: string; seconds?: string };\n}\n\nconst pad = (n: number) => String(n).padStart(2, \"0\");\n\n/** شمارش معکوس با ارقام فارسی؛ هر ثانیه به‌روز می‌شود و در پایان onComplete را صدا می‌زند */\nexport function Countdown({ to, seconds, onComplete, variant = \"clock\", digits, \"aria-label\": ariaLabel, className, labels = {} }: CountdownProps) {\n  const { messages: M, digits: D } = useKashi();\n  digits ??= D;\n  const { days = M.countdown.days, hours = M.countdown.hours, minutes = M.countdown.minutes, seconds: secLabel = M.countdown.seconds } = labels;\n  const end = useRef(to?.getTime() ?? Date.now() + (seconds ?? 0) * 1000);\n  const [left, setLeft] = useState(() => Math.max(0, Math.ceil((end.current - Date.now()) / 1000)));\n  const done = useRef(false);\n\n  useEffect(() => {\n    end.current = to?.getTime() ?? Date.now() + (seconds ?? 0) * 1000;\n    done.current = false;\n    const tick = () => {\n      const s = Math.max(0, Math.ceil((end.current - Date.now()) / 1000));\n      setLeft(s);\n      if (s === 0 && !done.current) {\n        done.current = true;\n        onComplete?.();\n      }\n    };\n    tick();\n    const t = setInterval(tick, 1000);\n    return () => clearInterval(t);\n  }, [to?.getTime(), seconds]);\n\n  const d = Math.floor(left / 86400), h = Math.floor((left % 86400) / 3600), m = Math.floor((left % 3600) / 60), s = left % 60;\n  const f = (x: string | number) => (digits === \"fa\" ? toFaDigits(String(x)) : String(x));\n  // متن کامل برای صفحه‌خوان؛ role=\"timer\" به‌طور پیش‌فرض هر ثانیه خوانده نمی‌شود\n  const spoken = [d && `${f(d)} ${days}`, h && `${f(h)} ${hours}`, m && `${f(m)} ${minutes}`, `${f(s)} ${secLabel}`].filter(Boolean).join(M.common.and);\n\n  if (variant === \"blocks\") {\n    const units: [number, string][] = [...(d ? [[d, days] as [number, string]] : []), [h, hours], [m, minutes], [s, secLabel]];\n    return (\n      <div role=\"timer\" aria-label={ariaLabel ? `${ariaLabel}: ${spoken}` : spoken} className={cx(\"k-countdown k-countdown--blocks\", className)} data-done={left === 0 ? \"\" : undefined}>\n        {units.map(([v, name]) => (\n          <span key={name} className=\"k-countdown__unit\" aria-hidden=\"true\">\n            <span className=\"k-countdown__num\">{f(pad(v))}</span>\n            <span className=\"k-countdown__name\">{name}</span>\n          </span>\n        ))}\n      </div>\n    );\n  }\n  const clock = (d ? `${d}:` : \"\") + (h || d ? `${pad(h)}:` : \"\") + `${pad(m)}:${pad(s)}`;\n  return (\n    <span role=\"timer\" aria-label={ariaLabel ? `${ariaLabel}: ${spoken}` : spoken} className={cx(\"k-countdown\", className)} data-done={left === 0 ? \"\" : undefined} dir=\"ltr\">\n      <span aria-hidden=\"true\">{f(clock)}</span>\n    </span>\n  );\n}\n"
    }
  ]
}