{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "explorer-01",
  "title": "Explorer 01",
  "description": "An organ viewer with an anatomy panel that docks beside it on wide screens and opens as a sheet on phones. Heart model licensed CC BY 4.0.",
  "dependencies": [
    "@base-ui/react@^1.8.0",
    "class-variance-authority@^0.7.1",
    "cn@^0.5.0"
  ],
  "registryDependencies": [
    "sheet",
    "toggle",
    "@organui/anatomy-tree",
    "@organui/heart",
    "@organui/organ-viewer"
  ],
  "files": [
    {
      "path": "registry/base-nova/blocks/explorer-01/page.tsx",
      "content": "import { OrganExplorer } from \"@/components/organ-explorer\"\nimport { heartModel } from \"@/components/heart\"\n\nexport default function Page() {\n  return (\n    <main className=\"mx-auto flex min-h-svh w-full max-w-5xl flex-col gap-6 p-6 md:p-10\">\n      <div className=\"flex flex-col gap-1\">\n        <h1 className=\"text-2xl font-semibold tracking-tight\">\n          {heartModel.name}\n        </h1>\n        <p className=\"text-muted-foreground\">\n          Rotate the model, or open the anatomy panel to find and hide\n          structures.\n        </p>\n      </div>\n      <OrganExplorer model={heartModel} />\n    </main>\n  )\n}\n",
      "type": "registry:page",
      "target": "app/explorer/page.tsx"
    },
    {
      "path": "registry/base-nova/blocks/explorer-01/components/organ-explorer.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { cn } from \"cn\"\n\nimport { AnatomyTree } from \"@/components/ui/anatomy-tree\"\nimport {\n  Sheet,\n  SheetContent,\n  SheetHeader,\n  SheetTitle,\n} from \"@/components/ui/sheet\"\nimport { Toggle } from \"@/components/ui/toggle\"\nimport {\n  OrganViewer,\n  organAnatomyNodes,\n  organDefaultVisibility,\n  type OrganModel,\n} from \"@/components/organ-viewer\"\n\n// Matches the lg breakpoint where the panel fits beside the viewer.\nconst WIDE = \"(min-width: 64rem)\"\nfunction subscribeWide(onChange: () => void) {\n  const query = matchMedia(WIDE)\n  query.addEventListener(\"change\", onChange)\n  return () => query.removeEventListener(\"change\", onChange)\n}\n\n// The anatomy tree drives the viewer, and picks in 3D flow back to the tree.\n// Like the explorers, the panel starts closed so the 3D view stands alone.\n// Phones open it as a sheet.\nexport function OrganExplorer({\n  model,\n  className,\n}: {\n  model: OrganModel\n  className?: string\n}) {\n  const [data] = React.useState(() => organAnatomyNodes(model))\n  const [selectedId, setSelectedId] = React.useState<string | null>(null)\n  const [visibility, setVisibility] = React.useState(() =>\n    organDefaultVisibility(model)\n  )\n  const [view, setView] = React.useState<string | null>(model.defaultView)\n  const selected = model.structures.find(({ id }) => id === selectedId)\n  const [open, setOpen] = React.useState(false)\n  const wide = React.useSyncExternalStore(\n    subscribeWide,\n    () => matchMedia(WIDE).matches,\n    () => true\n  )\n  const panel = React.useId()\n  const panelRef = React.useRef<HTMLDivElement>(null)\n  const sheetRef = React.useRef<HTMLDivElement>(null)\n  const toggle = React.useRef<HTMLButtonElement>(null)\n  React.useEffect(() => {\n    if (open && wide)\n      panelRef.current?.querySelector<HTMLElement>(\"input\")?.focus()\n  }, [open, wide])\n  const close = () => {\n    setOpen(false)\n    toggle.current?.focus()\n  }\n  const tree = (className: string) => (\n    <AnatomyTree\n      data={data}\n      selectedId={selectedId}\n      onSelectionChange={setSelectedId}\n      visibility={visibility}\n      onVisibilityChange={setVisibility}\n      label={`${model.name} anatomy`}\n      // Small organs open fully; the skeleton and muscles start collapsed.\n      defaultExpandedIds={\n        model.structures.length <= 40 ? data.map(({ id }) => id) : []\n      }\n      className={className}\n    />\n  )\n  return (\n    <div\n      className={cn(\n        \"grid gap-4\",\n        open && wide && \"grid-cols-[minmax(0,1fr)_18rem]\",\n        className\n      )}\n    >\n      <div className=\"flex min-w-0 flex-col gap-3\">\n        <OrganViewer\n          model={model}\n          selectedId={selectedId}\n          onSelectionChange={setSelectedId}\n          visibility={visibility}\n          onViewChange={setView}\n          className=\"h-[28rem]\"\n        />\n        <div className=\"flex items-start justify-between gap-4\">\n          <p className=\"text-sm text-muted-foreground\" role=\"status\">\n            {selected\n              ? `${selected.label}: ${selected.description ?? selected.group}`\n              : `View: ${view ?? \"Custom view\"}. Select a structure to learn about it.`}\n          </p>\n          <Toggle\n            ref={toggle}\n            variant=\"outline\"\n            pressed={open}\n            onPressedChange={(pressed) => (pressed ? setOpen(true) : close())}\n            aria-controls={open && wide ? panel : undefined}\n            className=\"shrink-0\"\n          >\n            <svg\n              width={16}\n              height={16}\n              viewBox=\"0 0 24 24\"\n              fill=\"none\"\n              stroke=\"currentColor\"\n              strokeWidth={2}\n              strokeLinecap=\"round\"\n              strokeLinejoin=\"round\"\n              aria-hidden=\"true\"\n              data-icon=\"inline-start\"\n            >\n              <rect width=\"18\" height=\"18\" x=\"3\" y=\"3\" rx=\"2\" />\n              <path d=\"M15 3v18\" />\n            </svg>\n            Anatomy\n          </Toggle>\n        </div>\n      </div>\n      {open && wide && (\n        <div\n          id={panel}\n          ref={panelRef}\n          onKeyDown={(event) => {\n            if (event.key === \"Escape\") close()\n          }}\n        >\n          {tree(\"max-h-[28rem] overflow-auto\")}\n        </div>\n      )}\n      <Sheet\n        open={open && !wide}\n        onOpenChange={(next) => {\n          if (!next) setOpen(false)\n        }}\n      >\n        <SheetContent\n          ref={sheetRef}\n          side=\"bottom\"\n          initialFocus={() => sheetRef.current?.querySelector(\"input\")}\n          finalFocus={toggle}\n          className=\"gap-0 data-[side=bottom]:h-[70svh]\"\n        >\n          <SheetHeader className=\"pr-12\">\n            <SheetTitle>{model.name} anatomy</SheetTitle>\n          </SheetHeader>\n          {tree(\n            \"min-h-0 flex-1 overflow-auto rounded-none border-x-0 border-b-0\"\n          )}\n        </SheetContent>\n      </Sheet>\n    </div>\n  )\n}\n",
      "type": "registry:component"
    }
  ],
  "docs": "Explorer 01: https://organui.com/blocks#explorer-01. Keep the organ viewer's model credit visible.",
  "categories": [
    "explorer"
  ],
  "type": "registry:block"
}