{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "browser-mockup",
  "type": "registry:component",
  "title": "Browser mockup",
  "description": "A browser window frame with traffic lights, back and forward buttons, an address bar and optional tabs, wrapping your screenshot or any children.",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "registry/premium/browser-mockup.tsx",
      "type": "registry:ui",
      "target": "components/ui/browser-mockup.tsx",
      "content": "\"use client\"\nimport * as React from \"react\"\nimport {\n  ChevronLeftIcon,\n  ChevronRightIcon,\n  EllipsisIcon,\n  FileTextIcon,\n  HouseIcon,\n  LockIcon,\n  RocketIcon,\n  RotateCwIcon,\n  SettingsIcon,\n  UsersIcon,\n  WifiIcon,\n  BatteryIcon,\n  SignalIcon,\n  ArrowUpRightIcon,\n  PlusIcon,\n  SendIcon,\n  ReceiptIcon,\n  ArrowDownLeftIcon,\n} from \"lucide-react\"\nimport { cn } from \"@/lib/utils\"\n\n/**\n * A browser window frame with traffic lights, back and forward buttons, an address bar and optional tabs, wrapping your screenshot or any children.\n * PhoneMockup, exported from the same file, is the matching handset with a\n * dynamic island and rounded screen. Both are built from tokens, so they\n * follow light, dark and every material. With no content, the browser shows\n * a Lumen dashboard made of plain divs, with a phone over its corner on wide\n * screens and below it on narrow ones.\n */\ntype BrowserMockupProps = {\n  /** Address shown in the URL bar. */\n  url?: string\n  /** Tab labels. The strip is hidden when empty. */\n  tabs?: string[]\n  /** Index of the active tab. */\n  activeTab?: number\n  /** Screenshot to show instead of children. */\n  src?: string\n  alt?: string\n  /** Stay dark in both themes, or follow the page theme. */\n  appearance?: \"auto\" | \"dark\"\n  /** Render a PhoneMockup beside the window. Defaults to on for the built-in sample only. */\n  phone?: boolean\n  children?: React.ReactNode\n  className?: string\n}\n\ntype PhoneMockupProps = {\n  /** Screenshot to show instead of children. */\n  src?: string\n  alt?: string\n  /** Clock in the status bar. */\n  time?: string\n  /** Width in pixels. Height follows the 9:19.5 screen ratio. */\n  width?: number\n  /** Stay dark in both themes, or follow the page theme. */\n  appearance?: \"auto\" | \"dark\"\n  children?: React.ReactNode\n  className?: string\n}\n\nfunction TrafficLights() {\n  return (\n    <span aria-hidden className=\"flex items-center gap-1.5\">\n      <span className=\"size-2.5 rounded-full bg-danger/80\" />\n      <span className=\"size-2.5 rounded-full bg-warning/80\" />\n      <span className=\"size-2.5 rounded-full bg-success/80\" />\n    </span>\n  )\n}\n\nfunction splitUrl(url: string) {\n  const m = url.match(/^(?:(https?:\\/\\/))?([^/?#]+)(.*)$/)\n  return m ? { scheme: m[1] ?? \"\", host: m[2], rest: m[3] } : { scheme: \"\", host: url, rest: \"\" }\n}\n\nconst BARS = [34, 48, 42, 58, 52, 66, 60, 74, 70, 84, 78, 92]\nconst INVOICES = [\n  { id: \"INV-2041\", name: \"Maya Chen\", amount: \"$4,200.00\", status: \"Paid\" },\n  { id: \"INV-2040\", name: \"Theo Okafor\", amount: \"$1,850.00\", status: \"Sent\" },\n  { id: \"INV-2039\", name: \"Priya Raman\", amount: \"$12,400.00\", status: \"Paid\" },\n]\nconst NAV = [\n  { icon: HouseIcon, label: \"Overview\", active: true },\n  { icon: FileTextIcon, label: \"Invoices\" },\n  { icon: UsersIcon, label: \"Customers\" },\n  { icon: RocketIcon, label: \"Deploys\" },\n  { icon: SettingsIcon, label: \"Settings\" },\n]\n\n/** The sample page: a slice of Lumen's overview, in plain divs. */\nfunction LumenDashboard() {\n  return (\n    <div data-slot=\"browser-mockup-sample\" className=\"flex h-[320px] text-[11px] leading-[1.5]\">\n      <aside className=\"hidden w-36 shrink-0 flex-col border-r border-border bg-surface p-3 @md:flex\">\n        <div className=\"flex items-center gap-2 px-1.5 pb-3\">\n          <span className=\"grid size-5 place-items-center rounded-md bg-ink text-[9px] font-semibold text-on-ink\">L</span>\n          <span className=\"text-xs font-medium text-fg\">Lumen</span>\n        </div>\n        <ul className=\"space-y-0.5\">\n          {NAV.map((n) => (\n            <li\n              key={n.label}\n              className={cn(\"flex h-7 items-center gap-2 rounded-md px-2\", n.active ? \"bg-sunken font-medium text-fg\" : \"text-fg-muted\")}\n            >\n              <n.icon className=\"size-3.5\" />\n              {n.label}\n            </li>\n          ))}\n        </ul>\n      </aside>\n      <main className=\"min-w-0 flex-1 space-y-3 overflow-hidden bg-bg p-4\">\n        <div className=\"flex items-center justify-between\">\n          <p className=\"text-sm font-medium tracking-[-0.01em] text-fg\">Overview</p>\n          <span className=\"rounded-md border border-border bg-surface px-2 py-0.5 text-[10px] text-fg-muted shadow-xs\">Last 30 days</span>\n        </div>\n        <div className=\"grid grid-cols-2 gap-2 @md:grid-cols-3\">\n          {[\n            { label: \"Revenue\", value: \"$128,400\", delta: \"+12.4%\", up: true },\n            { label: \"Open invoices\", value: \"24\", delta: \"-3\", up: false },\n            { label: \"New customers\", value: \"86\", delta: \"+18%\", up: true },\n          ].map((s, i) => (\n            <div key={s.label} className={cn(\"min-w-0 rounded-lg border border-border bg-surface p-2.5 shadow-raised\", i === 2 && \"hidden @md:block\")}>\n              <p className=\"truncate text-[10px] text-fg-muted\">{s.label}</p>\n              <p className=\"mt-0.5 text-sm font-medium tracking-[-0.01em] text-fg\">{s.value}</p>\n              <p className={cn(\"text-[10px]\", s.up ? \"text-success\" : \"text-fg-subtle\")}>{s.delta}</p>\n            </div>\n          ))}\n        </div>\n        <div className=\"grid gap-2 @md:grid-cols-[1fr_1.2fr]\">\n          <div className=\"hidden flex-col rounded-lg border border-border bg-surface p-2.5 shadow-raised @md:flex\">\n            <p className=\"text-[10px] text-fg-muted\">Revenue by week</p>\n            <div className=\"mt-2 flex min-h-20 flex-1 items-end gap-1\">\n              {BARS.map((h, i) => (\n                <span\n                  key={i}\n                  className={cn(\"flex-1 rounded-sm\", i === BARS.length - 1 ? \"bg-accent\" : \"bg-accent-soft\")}\n                  style={{ height: `${h}%` }}\n                />\n              ))}\n            </div>\n          </div>\n          <div className=\"rounded-lg border border-border bg-surface p-2.5 shadow-raised\">\n            <p className=\"text-[10px] text-fg-muted\">Recent invoices</p>\n            <ul className=\"mt-1.5 divide-y divide-border\">\n              {INVOICES.map((inv) => (\n                <li key={inv.id} className=\"flex items-center justify-between py-1.5\">\n                  <span className=\"min-w-0\">\n                    <span className=\"block truncate font-medium text-fg\">{inv.name}</span>\n                    <span className=\"font-mono text-[9.5px] text-fg-subtle\">{inv.id}</span>\n                  </span>\n                  <span className=\"text-right\">\n                    <span className=\"block font-mono text-fg\">{inv.amount}</span>\n                    <span className={cn(\"text-[9.5px]\", inv.status === \"Paid\" ? \"text-success\" : inv.status === \"Due\" ? \"text-warning\" : \"text-fg-muted\")}>\n                      {inv.status}\n                    </span>\n                  </span>\n                </li>\n              ))}\n            </ul>\n          </div>\n        </div>\n      </main>\n    </div>\n  )\n}\n\n/** The sample phone screen: Lumen's mobile home. */\nfunction LumenMobile() {\n  return (\n    <div data-slot=\"phone-mockup-sample\" className=\"flex h-full flex-col gap-3 bg-bg px-3 pt-2 text-[11px] leading-[1.5]\">\n      <div>\n        <p className=\"text-[10px] text-fg-muted\">Good morning</p>\n        <p className=\"text-sm font-medium tracking-[-0.01em] text-fg\">Maya Chen</p>\n      </div>\n      <div className=\"rounded-xl bg-ink p-3.5 text-on-ink shadow-ink\">\n        <p className=\"text-[10px] text-on-ink/70\">Available balance</p>\n        <p className=\"mt-0.5 font-mono text-lg font-medium tracking-[-0.01em]\">$42,180.00</p>\n        <p className=\"mt-1.5 inline-flex items-center gap-1 rounded-full bg-on-ink/10 px-1.5 py-0.5 text-[9.5px]\">\n          <ArrowUpRightIcon className=\"size-2.5\" /> +$6,240 this month\n        </p>\n      </div>\n      <div className=\"grid grid-cols-4 gap-2 text-center text-[9.5px] text-fg-muted\">\n        {[\n          { icon: SendIcon, label: \"Send\" },\n          { icon: PlusIcon, label: \"Invoice\" },\n          { icon: ReceiptIcon, label: \"Bills\" },\n          { icon: EllipsisIcon, label: \"More\" },\n        ].map((a) => (\n          <span key={a.label} className=\"flex flex-col items-center gap-1\">\n            <span className=\"grid size-8 place-items-center rounded-full border border-border bg-surface text-fg shadow-key\">\n              <a.icon className=\"size-3.5\" />\n            </span>\n            {a.label}\n          </span>\n        ))}\n      </div>\n      <div className=\"min-h-0 flex-1 rounded-t-xl border border-b-0 border-border bg-surface px-3 pt-2.5 shadow-raised\">\n        <p className=\"text-[10px] font-medium text-fg-muted\">Recent</p>\n        <ul className=\"mt-0.5 divide-y divide-border\">\n          {[\n            { name: \"Sam Lowe\", note: \"INV-2038 paid\", amount: \"+$960\", inbound: true },\n            { name: \"Northwind\", note: \"Hosting\", amount: \"-$240\", inbound: false },\n            { name: \"Maya Chen\", note: \"INV-2041 paid\", amount: \"+$4,200\", inbound: true },\n          ].map((r) => (\n            <li key={r.name} className=\"flex items-center gap-2 py-2\">\n              <span className=\"min-w-0 flex-1\">\n                <span className=\"block truncate font-medium text-fg\">{r.name}</span>\n                <span className=\"block truncate text-[9.5px] text-fg-subtle\">{r.note}</span>\n              </span>\n              <span className={cn(\"inline-flex shrink-0 items-center gap-0.5 font-mono text-[10.5px]\", r.inbound ? \"text-success\" : \"text-fg\")}>\n                {r.inbound ? <ArrowDownLeftIcon className=\"size-2.5\" /> : <ArrowUpRightIcon className=\"size-2.5\" />}\n                {r.amount}\n              </span>\n            </li>\n          ))}\n        </ul>\n      </div>\n    </div>\n  )\n}\n\nfunction PhoneMockup({ src, alt = \"\", time = \"9:41\", width = 240, appearance = \"auto\", children, className }: PhoneMockupProps) {\n  return (\n    <div\n      data-slot=\"phone-mockup\"\n      data-tier=\"premium\"\n      className={cn(appearance === \"dark\" && \"dark\", \"relative shrink-0 rounded-[44px] bg-ink p-2 shadow-lg\", className)}\n      style={{ width, maxWidth: \"100%\" }}\n    >\n      <span aria-hidden className=\"absolute top-20 -left-0.5 h-6 w-0.5 rounded-l-sm bg-ink\" />\n      <span aria-hidden className=\"absolute top-30 -left-0.5 h-10 w-0.5 rounded-l-sm bg-ink\" />\n      <span aria-hidden className=\"absolute top-42 -left-0.5 h-10 w-0.5 rounded-l-sm bg-ink\" />\n      <span aria-hidden className=\"absolute top-32 -right-0.5 h-14 w-0.5 rounded-r-sm bg-ink\" />\n      <div data-slot=\"phone-mockup-screen\" className=\"relative aspect-[9/19.5] overflow-hidden rounded-[36px] bg-bg text-fg ring-1 ring-border\">\n        <div aria-hidden className=\"pointer-events-none absolute inset-x-0 top-0 z-10 flex h-11 items-center justify-between px-6 pt-1 text-[11px] font-medium text-fg\">\n          <span>{time}</span>\n          <span className=\"flex items-center gap-1\">\n            <SignalIcon className=\"size-3\" />\n            <WifiIcon className=\"size-3\" />\n            <BatteryIcon className=\"size-3.5\" />\n          </span>\n        </div>\n        <span aria-hidden data-slot=\"phone-mockup-island\" className=\"absolute top-2.5 left-1/2 z-10 h-5 w-18 -translate-x-1/2 rounded-full bg-ink dark:bg-sunken\" />\n        <div className=\"absolute inset-0 pt-11\">\n          {src ? <img src={src} alt={alt} className=\"block size-full object-cover object-top\" /> : children ?? <LumenMobile />}\n        </div>\n      </div>\n    </div>\n  )\n}\n\nfunction BrowserMockup({\n  url = \"https://app.lumen.dev/overview\",\n  tabs = [],\n  activeTab = 0,\n  src,\n  alt = \"\",\n  appearance = \"auto\",\n  phone,\n  children,\n  className,\n}: BrowserMockupProps) {\n  const { host, rest } = splitUrl(url)\n  const sample = children === undefined && src === undefined\n  const showPhone = phone ?? sample\n\n  const frame = (\n    <div\n      data-slot=\"browser-mockup\"\n      data-tier=\"premium\"\n      className={cn(\n        !showPhone && appearance === \"dark\" && \"dark\",\n        \"@container min-w-0 w-full overflow-hidden rounded-xl border border-border bg-bg text-fg shadow-raised\",\n        !showPhone && className\n      )}\n    >\n      {tabs.length ? (\n        <div data-slot=\"browser-mockup-tabs\" className=\"flex items-end gap-1 overflow-hidden bg-sunken px-3 pt-2\">\n          {tabs.map((t, i) => (\n            <span\n              key={`${t}-${i}`}\n              aria-current={i === activeTab ? \"page\" : undefined}\n              className={cn(\n                \"max-w-40 truncate rounded-t-lg px-3 py-1.5 text-xs\",\n                i === activeTab ? \"bg-surface text-fg shadow-highlight\" : \"text-fg-muted\"\n              )}\n            >\n              {t}\n            </span>\n          ))}\n        </div>\n      ) : null}\n      <div data-slot=\"browser-mockup-bar\" className=\"flex h-12 items-center gap-3 border-b border-border bg-surface px-3\">\n        <TrafficLights />\n        <span aria-hidden className=\"hidden items-center gap-0.5 text-fg-subtle @sm:flex\">\n          <ChevronLeftIcon className=\"size-4\" />\n          <ChevronRightIcon className=\"size-4 opacity-40\" />\n          <RotateCwIcon className=\"ml-1 size-3.5\" />\n        </span>\n        <span data-slot=\"browser-mockup-address\" className=\"flex h-8 min-w-0 flex-1 items-center gap-2 rounded-lg bg-sunken px-3 font-mono text-xs\">\n          <LockIcon aria-hidden className=\"size-3 shrink-0 text-fg-subtle\" />\n          <span className=\"truncate\">\n            <span className=\"text-fg\">{host}</span>\n            <span className=\"text-fg-muted\">{rest}</span>\n          </span>\n        </span>\n        <span aria-hidden className=\"hidden text-fg-subtle @sm:block\">\n          <EllipsisIcon className=\"size-4\" />\n        </span>\n      </div>\n      <div data-slot=\"browser-mockup-viewport\" className=\"bg-bg\">\n        {src ? <img src={src} alt={alt} className=\"block w-full\" /> : sample ? <LumenDashboard /> : children}\n      </div>\n    </div>\n  )\n\n  if (!showPhone) return frame\n\n  // Wide containers: the phone overlaps the window's bottom right corner. Narrow ones stack.\n  return (\n    <div\n      data-slot=\"browser-mockup-scene\"\n      className={cn(appearance === \"dark\" && \"dark\", \"@container w-full\", className)}\n    >\n      <div className=\"relative @xl:pr-44 @xl:pb-8\">\n        {frame}\n        <PhoneMockup width={192} className=\"mx-auto mt-6 @xl:absolute @xl:right-0 @xl:bottom-0 @xl:mt-0\" />\n      </div>\n    </div>\n  )\n}\n\nexport { BrowserMockup, PhoneMockup }\nexport type { BrowserMockupProps, PhoneMockupProps }\n"
    }
  ]
}
