{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "download-1",
  "title": "Download 1: Mac app download with release notes",
  "author": "Ballmac <https://ui.ballmac.com>",
  "description": "A Mac app download section: app icon and version, an Apple silicon or Intel choice that swaps the file, copyable checksum and Homebrew command, install steps and release notes you can flip between.",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [
    "utils",
    "https://ui.ballmac.com/r/button.json",
    "https://ui.ballmac.com/r/copy-button.json",
    "https://ui.ballmac.com/r/mac-icons.json",
    "https://ui.ballmac.com/r/segmented-control.json",
    "https://ui.ballmac.com/r/i18n.json",
    "https://ui.ballmac.com/r/media.json"
  ],
  "files": [
    {
      "path": "registry/ballmac/components/blocks/download-1/download-1.tsx",
      "content": "// Ballmac UI: Download 1. https://ui.ballmac.com/blocks/download-1\n\"use client\"\n\nimport * as React from \"react\"\nimport { ArrowDownToLine, Bug, Check, Sparkles, Wand2 } from \"lucide-react\"\n\nimport { buttonVariants } from \"@/components/ballmac/button\"\nimport { CopyButton } from \"@/components/ballmac/copy-button\"\nimport { AppIcon } from \"@/components/ballmac/mac-icons\"\nimport { SegmentedControl, SegmentedControlItem } from \"@/components/ballmac/segmented-control\"\nimport { cn } from \"@/lib/utils\"\nimport { useLocale } from \"@/lib/ballmac/i18n\"\nimport { Media, type MediaSource } from \"@/components/ballmac/media\"\n\ntype Download1Arch = \"arm64\" | \"x64\"\ntype Download1Kind = \"new\" | \"improved\" | \"fixed\"\n\ntype Download1Release = {\n  version: string\n  /** Release date as an ISO date, shown in UTC. */\n  date: string\n  changes: { type: Download1Kind; text: string }[]\n}\n\ntype Download1Props = Omit<React.ComponentProps<\"section\">, \"title\"> & {\n  /** App name. */\n  app?: string\n  /** One sentence under the name. */\n  tagline?: string\n  /** One letter or short text drawn on the icon. */\n  glyph?: string\n  /** Your app icon instead of the sample tile: an image URL, an object with alt text and a dark-mode file, or your own element. A plain URL is described by the app name. */\n  icon?: MediaSource\n  /** Downloads per Mac type: the link, the file name and the size. */\n  downloads?: Record<Download1Arch, { href: string; file: string; size: string; sha256?: string }>\n  /** Requirements shown as chips. */\n  requirements?: string[]\n  /** Command for Homebrew users. Pass null to hide it. */\n  brew?: string | null\n  /** Install steps. Pass an empty array to hide them. */\n  steps?: string[]\n  /** Releases, newest first. The first one is the current version. */\n  releases?: Download1Release[]\n  /** Locale for dates. Fixed by default so server and browser match. */\n  locale?: string\n}\n\nconst kinds: Record<Download1Kind, { label: string; icon: typeof Sparkles; chip: string }> = {\n  new: { label: \"New\", icon: Sparkles, chip: \"bg-chart-2/15\" },\n  improved: { label: \"Improved\", icon: Wand2, chip: \"bg-chart-1/15\" },\n  fixed: { label: \"Fixed\", icon: Bug, chip: \"bg-chart-3/20\" },\n}\n\nconst defaultReleases: Download1Release[] = [\n  {\n    version: \"2.4.0\", date: \"2026-09-29\",\n    changes: [\n      { type: \"new\", text: \"Approve or reject invoices from the menu bar\" },\n      { type: \"new\", text: \"Daily digest notification with the three items that need you\" },\n      { type: \"improved\", text: \"Opens large ledgers about twice as fast\" },\n      { type: \"fixed\", text: \"Exports no longer drop the last row\" },\n    ],\n  },\n  {\n    version: \"2.3.2\", date: \"2026-09-18\",\n    changes: [\n      { type: \"fixed\", text: \"Dark mode charts were unreadable on some displays\" },\n      { type: \"improved\", text: \"Clearer message when a bank connection needs refreshing\" },\n    ],\n  },\n  {\n    version: \"2.3.0\", date: \"2026-09-04\",\n    changes: [\n      { type: \"new\", text: \"SAML single sign-on for teams\" },\n      { type: \"improved\", text: \"The audit log records which identity provider was used\" },\n    ],\n  },\n]\n\nconst defaultDownloads: NonNullable<Download1Props[\"downloads\"]> = {\n  arm64: { href: \"#\", file: \"Ledger-2.4.0-arm64.dmg\", size: \"58 MB\", sha256: \"9f2c71a0d4b8e53c1a6f07b2d9e8c4a15b3f6e0d7c2a9b84e1f5d3a60c7b2e91\" },\n  x64: { href: \"#\", file: \"Ledger-2.4.0-x64.dmg\", size: \"64 MB\", sha256: \"4b7e0a93c2d1f685e0a7b3c9d2f1840e6a5b97c3d0e2f4186a9b5c7d3e1f0a28\" },\n}\n\nfunction Download1({\n  app = \"Ledger\",\n  tagline = \"Invoices, approvals and reports, right in your menu bar.\",\n  glyph,\n  icon,\n  downloads = defaultDownloads,\n  requirements = [\"macOS 13 Ventura or later\", \"Apple silicon or Intel\", \"Signed and notarized\"],\n  brew = \"brew install --cask ledger\",\n  steps = [\"Open the downloaded disk image.\", \"Drag Ledger into your Applications folder.\", \"Open Ledger and sign in with your account.\"],\n  releases = defaultReleases,\n  locale,\n  className,\n  ...props\n}: Download1Props) {\n  const defaultLocale = useLocale()\n  locale ??= defaultLocale\n  const [arch, setArch] = React.useState<Download1Arch>(\"arm64\")\n  const [version, setVersion] = React.useState(releases[0]?.version ?? \"\")\n  const [started, setStarted] = React.useState(false)\n  const current = releases[0]\n  const shown = releases.find((r) => r.version === version) ?? current\n  const dl = downloads[arch]\n  const date = new Intl.DateTimeFormat(locale, { dateStyle: \"long\", timeZone: \"UTC\" })\n  const fmt = (iso: string) => date.format(new Date(`${iso}T00:00:00Z`))\n\n  return (\n    <section data-slot=\"download-1\" className={cn(\"mx-auto max-w-6xl px-4 py-20 sm:px-6 md:py-28\", className)} {...props}>\n      <div className=\"grid gap-12 lg:grid-cols-[1fr_1.05fr] lg:gap-16\">\n        <div className=\"min-w-0\">\n          <div className=\"flex items-center gap-5\">\n            {icon ? <Media media={icon} alt={app} aspect=\"square\" fit=\"contain\" priority className=\"size-[88px] shrink-0 rounded-[22%]\" /> : <AppIcon size={88} tone=\"blue\" className=\"text-3xl font-bold\">{glyph ?? app[0]}</AppIcon>}\n            <div>\n              <h2 className=\"text-3xl font-semibold tracking-[-0.035em] sm:text-4xl\">{app} for Mac</h2>\n              {current && <p className=\"text-muted-foreground mt-1 text-sm\">Version {current.version} · {fmt(current.date)}</p>}\n            </div>\n          </div>\n          <p className=\"text-muted-foreground mt-6 max-w-md text-lg text-pretty\">{tagline}</p>\n\n          <div className=\"bg-card mt-8 rounded-3xl border p-5 sm:p-6\">\n            <p id=\"download-1-arch\" className=\"mb-2 text-sm font-medium\">Which Mac do you have?</p>\n            <SegmentedControl aria-labelledby=\"download-1-arch\" value={arch} onValueChange={(v) => { setArch(v as Download1Arch); setStarted(false) }} fullWidth>\n              <SegmentedControlItem value=\"arm64\">Apple silicon</SegmentedControlItem>\n              <SegmentedControlItem value=\"x64\">Intel</SegmentedControlItem>\n            </SegmentedControl>\n            <p className=\"text-muted-foreground mt-2 text-xs\">Not sure? Choose Apple menu, About This Mac. Chips named M1 or later are Apple silicon.</p>\n            <a href={dl.href} onClick={() => setStarted(true)} className={buttonVariants({ size: \"lg\", shape: \"pill\", className: \"mt-5 w-full\" })}>\n              <ArrowDownToLine /> Download {app} <span className=\"opacity-70\">· {dl.size}</span>\n            </a>\n            <p role=\"status\" className=\"text-muted-foreground mt-3 min-h-5 text-center text-xs\">\n              {started ? `Your download should start in a moment (${dl.file}).` : dl.file}\n            </p>\n            {requirements.length > 0 && (\n              <ul className=\"mt-4 flex flex-wrap gap-2 border-t pt-4\">\n                {requirements.map((r) => (\n                  <li key={r} className=\"bg-muted/60 flex items-center gap-1.5 rounded-full px-3 py-1 text-xs\"><Check className=\"size-3\" aria-hidden=\"true\" />{r}</li>\n                ))}\n              </ul>\n            )}\n          </div>\n\n          <div className=\"mt-4 space-y-3\">\n            {dl.sha256 && (\n              <div className=\"flex items-center gap-3 rounded-2xl border px-4 py-2.5\">\n                <span className=\"text-muted-foreground shrink-0 text-xs font-medium\">SHA-256</span>\n                <code className=\"min-w-0 flex-1 truncate font-mono text-xs\" title={dl.sha256}>{dl.sha256}</code>\n                <CopyButton value={dl.sha256} ariaLabel=\"Copy SHA-256 checksum\" variant=\"ghost\" size=\"default\" />\n              </div>\n            )}\n            {brew && (\n              <div className=\"flex items-center gap-3 rounded-2xl border px-4 py-2.5\">\n                <span className=\"text-muted-foreground shrink-0 text-xs font-medium\">Homebrew</span>\n                <code className=\"min-w-0 flex-1 truncate font-mono text-xs\">{brew}</code>\n                <CopyButton value={brew} ariaLabel=\"Copy Homebrew command\" variant=\"ghost\" size=\"default\" />\n              </div>\n            )}\n          </div>\n\n          {steps.length > 0 && (\n            <ol className=\"mt-8 space-y-4\">\n              {steps.map((s, i) => (\n                <li key={s} className=\"flex items-start gap-3 text-sm\">\n                  <span aria-hidden=\"true\" className=\"bg-foreground text-background flex size-6 shrink-0 items-center justify-center rounded-full text-xs font-semibold tabular-nums\">{i + 1}</span>\n                  <span className=\"pt-0.5\">{s}</span>\n                </li>\n              ))}\n            </ol>\n          )}\n        </div>\n\n        {shown && (\n          <div className=\"bg-card min-w-0 self-start rounded-3xl border p-5 sm:p-7\">\n            <div className=\"flex flex-wrap items-center justify-between gap-3\">\n              <h3 className=\"text-lg font-semibold tracking-tight\">Release notes</h3>\n              {releases.length > 1 && (\n                <SegmentedControl aria-label=\"Version\" size=\"sm\" value={shown.version} onValueChange={setVersion}>\n                  {releases.map((r) => <SegmentedControlItem key={r.version} value={r.version}>{r.version}</SegmentedControlItem>)}\n                </SegmentedControl>\n              )}\n            </div>\n            <p className=\"text-muted-foreground mt-1 text-sm\">{app} {shown.version} · {fmt(shown.date)}{shown === current ? \" · latest\" : \"\"}</p>\n            <ul className=\"mt-6 space-y-3.5\">\n              {shown.changes.map((c) => {\n                const k = kinds[c.type]\n                return (\n                  <li key={c.text} className=\"flex items-start gap-3 text-[15px] leading-6\">\n                    <span className={cn(\"mt-0.5 inline-flex h-6 w-[5.5rem] shrink-0 items-center gap-1.5 rounded-full px-2 text-xs font-medium\", k.chip)}>\n                      <k.icon className=\"size-3.5\" aria-hidden=\"true\" />{k.label}\n                    </span>\n                    <span className=\"text-pretty\">{c.text}</span>\n                  </li>\n                )\n              })}\n            </ul>\n          </div>\n        )}\n      </div>\n    </section>\n  )\n}\n\nexport { Download1, type Download1Props, type Download1Release, type Download1Arch }\n",
      "type": "registry:component",
      "target": "@components/ballmac/blocks/download-1/download-1.tsx"
    }
  ],
  "meta": {
    "schema": 1,
    "tier": "free",
    "tags": [
      "download",
      "mac",
      "app",
      "release notes",
      "homebrew",
      "checksum",
      "install"
    ],
    "version": "1.0.0",
    "updated": "2026-10-01",
    "ai": {
      "summary": "The download page of a Mac app. Pass app, tagline, downloads={ arm64: { href, file, size, sha256? }, x64: {...} }, requirements, brew, steps and releases=[{ version, date, changes: [{ type, text }] }].",
      "whenToUse": [
        "Direct-download Mac apps",
        "Any desktop app with separate builds per chip"
      ],
      "whenNotToUse": [
        "App Store only apps (link to the store instead)",
        "Web apps"
      ],
      "composesWith": [
        "hero-5",
        "showcase-1",
        "features-7",
        "pricing-4"
      ],
      "a11y": [
        {
          "keys": "Arrow Left / Right",
          "action": "Switches between Apple silicon and Intel, and between release versions"
        },
        {
          "keys": "Download link",
          "action": "A real link; when activated a polite status confirms the file name"
        },
        {
          "keys": "Copy buttons",
          "action": "Named 'Copy SHA-256 checksum' and 'Copy Homebrew command'; success is announced"
        }
      ],
      "customization": [
        "downloads, requirements, brew (null hides), steps ([] hides), releases",
        "glyph: a letter or short text on the icon"
      ]
    },
    "examples": [
      "download-1-demo",
      "download-1-simple"
    ],
    "url": "https://ui.ballmac.com/components/download-1"
  },
  "categories": [
    "blocks",
    "download"
  ],
  "type": "registry:block"
}