{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "app-shell",
  "title": "App Shell",
  "description": "The nx application shell: fixed sidebar, main content card and page chrome, with an optional top bar carrying scope breadcrumbs and actions above the page card.",
  "dependencies": [
    "@untitledui/icons@^0.0.22"
  ],
  "registryDependencies": [
    "@nx-ui/button",
    "@nx-ui/sidebar",
    "@nx-ui/tooltip",
    "@nx-ui/utils"
  ],
  "files": [
    {
      "path": "components/blocks/app-shell.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { HelpCircle } from \"@untitledui/icons\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Button } from \"@/components/ui/button\"\nimport {\n  SidebarInset,\n  SidebarProvider,\n  SidebarTrigger,\n} from \"@/components/ui/sidebar\"\nimport {\n  Tooltip,\n  TooltipContent,\n  TooltipProvider,\n  TooltipTrigger,\n} from \"@/components/ui/tooltip\"\n\n/**\n * AppShell — the nx application chrome: a coloured gutter holding the sidebar on\n * the left and, on the right, the main content mounted inside a rounded page\n * card that is itself the scroll container. Recreates the look of the nx\n * `(shell)/layout.tsx` — a `bg-sidebar` gutter with a `bg-background` card that\n * gains `lg:rounded-3xl`, a `lg:ring-1 lg:ring-border` inset ring and a soft\n * `shadow-xs` from the large breakpoint up.\n *\n * Compose it with AppSidebar (or any Sidebar) as the `sidebar` slot:\n *\n *   <AppShell sidebar={<AppSidebar … />}>{page}</AppShell>\n *\n * Optionally give it a `topBar` — the nx header strip that sits ON THE GUTTER,\n * above the page card: scope breadcrumbs on the left, actions on the right\n * (a tooltip'd help icon-button by default). The bar is part of the shell, not\n * the page, so it never scrolls with the card:\n *\n *   <AppShell\n *     sidebar={…}\n *     topBar={{ left: <ScopeBreadcrumbs … />, onHelp: openHelp }}\n *   >\n *     {page}\n *   </AppShell>\n *\n * The gutter colour comes from the provider (`has-data-[variant=inset]`), so\n * pass an inset-variant sidebar — AppSidebar defaults to it.\n */\n\ntype AppShellTopBar = {\n  /** Left slot — typically a ScopeBreadcrumbs trail. */\n  left?: React.ReactNode\n  /**\n   * Right slot — action buttons. When omitted, a help icon-button renders\n   * (wired to `onHelp` when given).\n   */\n  right?: React.ReactNode\n  /** Click handler for the default help button (ignored if `right` is set). */\n  onHelp?: () => void\n}\n\n/** The default top-bar action: a tooltip'd help icon-button, as in nx. */\nfunction TopBarHelpButton({ onClick }: { onClick?: () => void }) {\n  return (\n    <TooltipProvider delay={200}>\n      <Tooltip>\n        <TooltipTrigger\n          render={\n            <Button\n              variant=\"ghost\"\n              size=\"icon-sm\"\n              aria-label=\"Help and feedback\"\n              onClick={onClick}\n            />\n          }\n        >\n          <HelpCircle className=\"size-5 text-fg-quaternary\" />\n        </TooltipTrigger>\n        <TooltipContent>Help and feedback</TooltipContent>\n      </Tooltip>\n    </TooltipProvider>\n  )\n}\n\nfunction AppShell({\n  sidebar,\n  topBar,\n  children,\n  defaultOpen = true,\n  className,\n  ...props\n}: React.ComponentProps<typeof SidebarProvider> & {\n  sidebar: React.ReactNode\n  /** Optional nx header strip above the page card (breadcrumbs ▸ actions). */\n  topBar?: AppShellTopBar\n}) {\n  return (\n    <SidebarProvider\n      defaultOpen={defaultOpen}\n      // Lock the shell to the viewport from lg up so the page card (not the\n      // window) owns scrolling — below lg the document scrolls as normal.\n      className={cn(\"lg:h-svh\", className)}\n      {...props}\n    >\n      {sidebar}\n      {topBar ? (\n        // Top-bar arrangement: SidebarInset stays the sidebar's peer (so the\n        // inset margins/collapse rules keep working) but turns transparent and\n        // hosts two children — the h-12 header strip on the gutter, then the\n        // rounded page card, which takes over the card treatment and scrolling.\n        <SidebarInset\n          className={cn(\n            \"bg-transparent min-h-svh lg:min-h-0\",\n            // Neutralise the primitive's inset card treatment on the wrapper —\n            // the inner page card carries it instead. Keep the m-2 side/bottom\n            // margins; the top margin collapses to zero so the bar owns the top.\n            \"md:peer-data-[variant=inset]:mt-0 md:peer-data-[variant=inset]:rounded-none md:peer-data-[variant=inset]:shadow-none\"\n          )}\n        >\n          <header\n            data-slot=\"app-shell-top-bar\"\n            className=\"flex h-12 shrink-0 items-center gap-2 px-3 lg:px-4\"\n          >\n            <SidebarTrigger className=\"md:hidden\" />\n            <div className=\"flex min-w-0 flex-1 items-center overflow-x-auto\">\n              {topBar.left}\n            </div>\n            <div className=\"flex shrink-0 items-center gap-1\">\n              {topBar.right ?? <TopBarHelpButton onClick={topBar.onHelp} />}\n            </div>\n          </header>\n          <div\n            data-slot=\"app-shell-page-card\"\n            className=\"relative flex min-h-0 w-full flex-1 flex-col bg-background md:rounded-3xl md:shadow-xs md:ring-1 md:ring-border lg:overflow-y-auto lg:overscroll-contain\"\n          >\n            {children}\n          </div>\n        </SidebarInset>\n      ) : (\n        <SidebarInset\n          className={cn(\n            // The page card: a scroll container that becomes a rounded, ringed,\n            // shadowed panel inset from the gutter at lg. Overrides the primitive's\n            // default rounded-xl/shadow-sm inset treatment. min-h-0 lets the flex\n            // child shrink to the locked shell height so overflow-y-auto engages.\n            \"bg-background shadow-xs md:peer-data-[variant=inset]:rounded-3xl md:peer-data-[variant=inset]:shadow-xs md:peer-data-[variant=inset]:ring-1 md:peer-data-[variant=inset]:ring-border lg:min-h-0 lg:overflow-y-auto lg:overscroll-contain\"\n          )}\n        >\n          {children}\n        </SidebarInset>\n      )}\n    </SidebarProvider>\n  )\n}\n\nexport { AppShell, TopBarHelpButton }\nexport type { AppShellTopBar }\n",
      "type": "registry:component"
    }
  ],
  "type": "registry:block"
}