{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "showcase-1",
  "title": "Showcase 1: interactive Mac desktop",
  "author": "Ballmac <https://ui.ballmac.com>",
  "description": "A working Mac desktop for your landing page: wallpaper, a menu bar with menus and a clock, draggable and resizable windows, desktop widgets and a magnifying dock that launches and relaunches them.",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [
    "utils",
    "https://ui.ballmac.com/r/dock.json",
    "https://ui.ballmac.com/r/menu-bar.json",
    "https://ui.ballmac.com/r/widgets.json",
    "https://ui.ballmac.com/r/window-manager.json"
  ],
  "files": [
    {
      "path": "registry/ballmac/components/blocks/showcase-1/showcase-1.tsx",
      "content": "// Ballmac UI: Showcase 1. https://ui.ballmac.com/blocks/showcase-1\n\"use client\"\n\nimport * as React from \"react\"\nimport { BatteryFull, Command, Mail, Music2, Search, StickyNote, Wifi } from \"lucide-react\"\n\nimport { Dock, DockItem, DockSeparator } from \"@/components/ballmac/dock\"\nimport { MenuBar, MenuBarClock, MenuBarContent, MenuBarItem, MenuBarMenu, MenuBarSeparator, MenuBarShortcut, MenuBarStatus, MenuBarStatusItem, MenuBarTrigger } from \"@/components/ballmac/menu-bar\"\nimport { WindowManager, type ManagedWindow } from \"@/components/ballmac/window-manager\"\nimport { CalendarWidget, WeatherWidget } from \"@/components/ballmac/widgets\"\nimport { cn } from \"@/lib/utils\"\n\ntype Showcase1Props = Omit<React.ComponentProps<\"div\">, \"children\"> & {\n  /** Name of your app, shown in the menu bar and the window title. */\n  app?: string\n  /** Your app's window content. It is laid out inside a container, so use container queries (`@sm:`) to adapt. Defaults to a sample ledger. */\n  children?: React.ReactNode\n  /** The time shown in the menu bar, as an ISO date-time. Fixed so every viewer sees the same screen. */\n  time?: string\n  /** Show the weather and calendar widgets on wide screens. */\n  widgets?: boolean\n  /** Include the sample Notes window and its dock icon. Turn it off when you show your own app on its own. */\n  notes?: boolean\n  /** Height of the scene. */\n  height?: string\n}\n\nconst tile = \"text-white [&_svg]:drop-shadow-[0_1px_1px_rgb(0_0_0/0.25)]\"\n\nfunction Ledger({ app }: { app: string }) {\n  const rows = [\n    [\"Northwind Studio\", \"Paid\", \"$4,200\"],\n    [\"Globex Corp\", \"Due\", \"$12,800\"],\n    [\"Initech\", \"Paid\", \"$960\"],\n    [\"Umbrella Labs\", \"Paid\", \"$3,450\"],\n  ]\n  return (\n    <div className=\"@container bg-background flex h-full text-xs\">\n      <aside className=\"bg-muted/50 hidden w-32 shrink-0 space-y-0.5 border-e p-2 @md:block\">\n        <p className=\"text-muted-foreground px-2 pt-1 pb-1.5 text-[10px] font-semibold uppercase\">{app}</p>\n        {[\"Overview\", \"Invoices\", \"Customers\", \"Reports\"].map((l, i) => (\n          <p key={l} className={cn(\"rounded-md px-2 py-1.5\", i === 1 ? \"bg-foreground/10 font-medium\" : \"text-muted-foreground\")}>{l}</p>\n        ))}\n      </aside>\n      <div className=\"min-w-0 flex-1 p-4\">\n        <p className=\"text-base font-semibold tracking-tight\">Invoices</p>\n        <div className=\"mt-3 grid grid-cols-3 gap-2\">\n          {[[\"Outstanding\", \"$12.8k\"], [\"Paid this month\", \"$48.2k\"], [\"Overdue\", \"2\"]].map(([k, v]) => (\n            <div key={k} className=\"rounded-lg border p-2\">\n              <p className=\"text-muted-foreground text-[10px]\">{k}</p>\n              <p className=\"mt-0.5 text-sm font-semibold tabular-nums\">{v}</p>\n            </div>\n          ))}\n        </div>\n        <ul className=\"mt-3 divide-y rounded-lg border\">\n          {rows.map(([n, s, a]) => (\n            <li key={n} className=\"flex items-center gap-2 px-2.5 py-2\">\n              <span className=\"min-w-0 flex-1 truncate font-medium\">{n}</span>\n              <span className={cn(\"rounded-full px-1.5 py-px text-[10px]\", s === \"Paid\" ? \"bg-chart-2/15\" : \"bg-chart-3/20\")}>{s}</span>\n              <span className=\"w-14 text-end tabular-nums\">{a}</span>\n            </li>\n          ))}\n        </ul>\n      </div>\n    </div>\n  )\n}\n\nfunction Notes() {\n  return (\n    <div className=\"bg-chart-3/10 h-full p-3 text-xs leading-relaxed\">\n      <p className=\"text-sm font-semibold\">Before launch</p>\n      <ul className=\"mt-2 space-y-1.5\">\n        {[[\"Final screenshots\", true], [\"Update the changelog\", true], [\"Notarize the build\", false], [\"Post to the newsletter\", false]].map(([t, done]) => (\n          <li key={String(t)} className=\"flex items-center gap-2\">\n            <span aria-hidden=\"true\" className={cn(\"flex size-3.5 items-center justify-center rounded-full border\", done && \"bg-chart-2 border-transparent\")}>{done && <svg viewBox=\"0 0 12 12\" className=\"size-2.5 text-white\"><path d=\"M2.5 6.5l2.5 2.5 4.5-5\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" /></svg>}</span>\n            <span className={cn(done && \"text-muted-foreground line-through\")}>{t}</span>\n          </li>\n        ))}\n      </ul>\n    </div>\n  )\n}\n\nfunction Showcase1({ app = \"Ledger\", children, time = \"2026-10-01T09:41:00\", widgets = true, notes = true, height = \"38rem\", className, style, ...props }: Showcase1Props) {\n  const ref = React.useRef<HTMLDivElement>(null)\n  const windowFor = React.useCallback(\n    (id: string, narrow: boolean): ManagedWindow => {\n      if (id === \"notes\") return { id, title: \"Notes\", x: 640, y: 60, width: 250, height: 220, content: <Notes /> }\n      return narrow\n        ? { id: \"app\", title: app, x: 10, y: 10, width: 330, height: 290, content: children ?? <Ledger app={app} /> }\n        : { id: \"app\", title: app, x: 56, y: 28, width: 560, height: 350, content: children ?? <Ledger app={app} /> }\n    },\n    [app, children]\n  )\n  const [narrow, setNarrow] = React.useState(false)\n  const [windows, setWindows] = React.useState<ManagedWindow[]>(() => (notes ? [windowFor(\"app\", false), windowFor(\"notes\", false)] : [windowFor(\"app\", false)]))\n  const open = React.useRef<string[]>(notes ? [\"app\", \"notes\"] : [\"app\"])\n\n  // Re-lay the windows out when the scene becomes narrow or wide. The observer callback is not part of rendering.\n  React.useEffect(() => {\n    const node = ref.current\n    if (!node || typeof ResizeObserver === \"undefined\") return\n    let last = false\n    const observer = new ResizeObserver(([entry]) => {\n      const isNarrow = entry.contentRect.width < 640\n      if (isNarrow === last) return\n      last = isNarrow\n      setNarrow(isNarrow)\n      setWindows(open.current.filter((id) => !(isNarrow && id === \"notes\")).map((id) => windowFor(id, isNarrow)))\n    })\n    observer.observe(node)\n    return () => observer.disconnect()\n  }, [windowFor])\n\n  const launch = (id: \"app\" | \"notes\") => {\n    if (narrow && id === \"notes\") return\n    if (!open.current.includes(id)) open.current = [...open.current, id]\n    setWindows((all) => (all.some((w) => w.id === id) ? all : [...all, windowFor(id, narrow)]))\n  }\n  const close = (id: string) => {\n    open.current = open.current.filter((x) => x !== id)\n    setWindows((all) => all.filter((w) => w.id !== id))\n  }\n  const running = (id: string) => windows.some((w) => w.id === id)\n\n  return (\n    <div\n      ref={ref}\n      data-slot=\"showcase-1\"\n      className={cn(\"@container relative isolate overflow-hidden rounded-2xl border border-foreground/10 shadow-[0_40px_100px_-50px_rgb(0_0_0/0.5)]\", className)}\n      style={{ height, ...style }}\n      {...props}\n    >\n      <div aria-hidden=\"true\" className=\"pointer-events-none absolute inset-0 -z-10 dark:brightness-[0.6] dark:saturate-[1.2]\">\n        <div className=\"absolute inset-0 bg-linear-to-br from-chart-1 via-chart-4 to-chart-5\" />\n        <div className=\"absolute inset-0 bg-[radial-gradient(70%_60%_at_15%_100%,var(--chart-2),transparent_70%),radial-gradient(60%_55%_at_85%_0%,var(--chart-3),transparent_70%)] opacity-70\" />\n        <div className=\"absolute -inset-x-1/4 top-[40%] h-[70%] rounded-[50%] bg-white/15 blur-2xl\" />\n      </div>\n\n      <MenuBar className=\"relative z-30\" aria-label=\"Menu bar\">\n        <MenuBarMenu value=\"system\">\n          <MenuBarTrigger aria-label=\"System menu\" className=\"px-2.5\"><Command aria-hidden=\"true\" /></MenuBarTrigger>\n          <MenuBarContent portal={false}>\n            <MenuBarItem>About This Mac</MenuBarItem>\n            <MenuBarSeparator />\n            <MenuBarItem>System Settings…</MenuBarItem>\n          </MenuBarContent>\n        </MenuBarMenu>\n        <MenuBarMenu value=\"app\">\n          <MenuBarTrigger variant=\"app\">{app}</MenuBarTrigger>\n          <MenuBarContent portal={false}>\n            <MenuBarItem>About {app}</MenuBarItem>\n            <MenuBarSeparator />\n            <MenuBarItem>Settings… <MenuBarShortcut>⌘,</MenuBarShortcut></MenuBarItem>\n            <MenuBarItem>Quit {app} <MenuBarShortcut>⌘Q</MenuBarShortcut></MenuBarItem>\n          </MenuBarContent>\n        </MenuBarMenu>\n        <MenuBarMenu value=\"file\">\n          <MenuBarTrigger className=\"max-sm:hidden\">File</MenuBarTrigger>\n          <MenuBarContent portal={false}>\n            <MenuBarItem>New Invoice <MenuBarShortcut>⌘N</MenuBarShortcut></MenuBarItem>\n            <MenuBarItem>Export… <MenuBarShortcut>⌘E</MenuBarShortcut></MenuBarItem>\n          </MenuBarContent>\n        </MenuBarMenu>\n        <MenuBarMenu value=\"window\">\n          <MenuBarTrigger className=\"max-sm:hidden\">Window</MenuBarTrigger>\n          <MenuBarContent portal={false}>\n            <MenuBarItem>Minimize <MenuBarShortcut>⌘M</MenuBarShortcut></MenuBarItem>\n            <MenuBarItem>Bring All to Front</MenuBarItem>\n          </MenuBarContent>\n        </MenuBarMenu>\n        <MenuBarStatus>\n          <MenuBarStatusItem aria-label=\"Wi-Fi\" className=\"max-sm:hidden\"><Wifi className=\"size-3.5\" aria-hidden=\"true\" /></MenuBarStatusItem>\n          <MenuBarStatusItem aria-label=\"Battery\"><BatteryFull className=\"size-4\" aria-hidden=\"true\" /></MenuBarStatusItem>\n          <MenuBarStatusItem aria-label=\"Search\" className=\"max-sm:hidden\"><Search className=\"size-3.5\" aria-hidden=\"true\" /></MenuBarStatusItem>\n          <MenuBarClock value={new Date(time)} className=\"max-sm:hidden\" />\n        </MenuBarStatus>\n      </MenuBar>\n\n      {widgets && (\n        <div className=\"pointer-events-none absolute top-10 end-4 z-0 hidden flex-col gap-3 @4xl:flex\">\n          <WeatherWidget city=\"Cupertino\" temperature={72} condition=\"partly-cloudy\" high={78} low={61} className=\"pointer-events-auto w-40\" />\n          <CalendarWidget date=\"2026-10-01\" events={[{ id: \"a\", title: \"Design review\", time: \"10:30 AM\", tone: \"red\" }]} className=\"pointer-events-auto w-40\" />\n        </div>\n      )}\n\n      <WindowManager windows={windows} onClose={close} label={`${app} desktop`} className=\"absolute inset-x-0 top-7 bottom-0 z-10\" />\n\n      <div className=\"pointer-events-none absolute inset-x-0 bottom-2 z-20 flex justify-center px-2\">\n        <Dock size={40} magnification={60} distance={130} aria-label=\"Applications\" className=\"pointer-events-auto\">\n          <DockItem label={app} active={running(\"app\")} activeLabel=\"running\" bounce onClick={() => launch(\"app\")} icon={<span className=\"text-lg font-bold\">{app[0]}</span>} className={`${tile} bg-linear-to-b from-chart-1/80 to-chart-1`} />\n          {notes && <DockItem label=\"Notes\" active={running(\"notes\")} activeLabel=\"running\" bounce onClick={() => launch(\"notes\")} icon={<StickyNote />} containerClassName=\"max-sm:hidden\" className={`${tile} bg-linear-to-b from-chart-3/80 to-chart-3`} />}\n          <DockItem label=\"Mail\" icon={<Mail />} bounce className={`${tile} bg-linear-to-b from-chart-1/60 to-chart-5`} />\n          <DockSeparator />\n          <DockItem label=\"Music\" icon={<Music2 />} bounce className={`${tile} bg-linear-to-b from-chart-4/80 to-chart-4`} />\n        </Dock>\n      </div>\n    </div>\n  )\n}\n\nexport { Showcase1, type Showcase1Props }\n",
      "type": "registry:component",
      "target": "@components/ballmac/blocks/showcase-1/showcase-1.tsx"
    }
  ],
  "meta": {
    "schema": 1,
    "tier": "free",
    "tags": [
      "mac",
      "desktop",
      "showcase",
      "windows",
      "dock",
      "menu bar",
      "demo"
    ],
    "version": "1.0.0",
    "updated": "2026-10-01",
    "ai": {
      "summary": "A hero-sized interactive screenshot. Set app, time (ISO, fixed) and pass your own window content as children; widgets={false} hides the desk widgets and notes={false} the sample Notes window. Windows can be dragged, resized, zoomed and minimized; the dock reopens closed apps.",
      "whenToUse": [
        "Landing pages for Mac apps that want to be tried, not just seen",
        "Under a hero as a live product tour"
      ],
      "whenNotToUse": [
        "Showing one screenshot (use laptop-frame or browser-frame)",
        "Non-desktop products"
      ],
      "composesWith": [
        "hero-5",
        "download-1",
        "features-7",
        "devices-1"
      ],
      "a11y": [
        {
          "keys": "Arrow Left / Right in the dock",
          "action": "Moves between dock items; Enter launches or reopens an app"
        },
        {
          "keys": "Window title bars",
          "action": "Windows can be moved and resized with the keyboard through the window manager; traffic lights are real buttons"
        },
        {
          "keys": "Menu bar",
          "action": "Arrow keys move between menus; Escape closes"
        }
      ],
      "customization": [
        "app, time, widgets, notes, height",
        "children: your window content, laid out in a container (use @sm: / @md: variants)"
      ]
    },
    "examples": [
      "showcase-1-demo",
      "showcase-1-custom"
    ],
    "url": "https://ui.ballmac.com/components/showcase-1"
  },
  "categories": [
    "blocks",
    "showcase"
  ],
  "type": "registry:block"
}