{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "sidebar",
  "type": "registry:ui",
  "title": "Sidebar",
  "description": "Collapsible app sidebar — a recessed well the active session rises out of; ⌘B rail collapse.",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [
    "https://seamui.dev/r/utils.json",
    "https://seamui.dev/r/button.json",
    "https://seamui.dev/r/badge.json"
  ],
  "files": [
    {
      "path": "registry/seam/ui/sidebar.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { PanelLeft } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Badge } from \"./badge\"\nimport { Button } from \"./button\"\n\ntype SidebarContextValue = {\n  open: boolean\n  setOpen: (open: boolean) => void\n  toggle: () => void\n}\n\nconst SidebarContext = React.createContext<SidebarContextValue | null>(null)\n\nfunction useSidebar() {\n  const ctx = React.useContext(SidebarContext)\n  if (!ctx) throw new Error(\"useSidebar must be used within <SidebarProvider>\")\n  return ctx\n}\n\n// Owns the expanded/collapsed state and the ⌘/Ctrl+B shortcut. The wrapper\n// carries data-state so the panel (and anything else) can style off it.\nfunction SidebarProvider({\n  defaultOpen = true,\n  open: openProp,\n  onOpenChange,\n  className,\n  children,\n  ...props\n}: React.ComponentProps<\"div\"> & {\n  defaultOpen?: boolean\n  open?: boolean\n  onOpenChange?: (open: boolean) => void\n}) {\n  const [openState, setOpenState] = React.useState(defaultOpen)\n  const open = openProp ?? openState\n\n  const setOpen = React.useCallback(\n    (next: boolean) => {\n      if (openProp === undefined) setOpenState(next)\n      onOpenChange?.(next)\n    },\n    [openProp, onOpenChange]\n  )\n\n  React.useEffect(() => {\n    const onKeyDown = (e: KeyboardEvent) => {\n      if (\n        e.key === \"b\" &&\n        (e.metaKey || e.ctrlKey) &&\n        !e.shiftKey &&\n        !e.altKey\n      ) {\n        e.preventDefault()\n        setOpen(!open)\n      }\n    }\n    window.addEventListener(\"keydown\", onKeyDown)\n    return () => window.removeEventListener(\"keydown\", onKeyDown)\n  }, [open, setOpen])\n\n  const value = React.useMemo(\n    () => ({ open, setOpen, toggle: () => setOpen(!open) }),\n    [open, setOpen]\n  )\n\n  return (\n    <SidebarContext.Provider value={value}>\n      <div\n        data-slot=\"sidebar-provider\"\n        data-state={open ? \"expanded\" : \"collapsed\"}\n        className={cn(\"group/sidebar flex min-h-0 w-full\", className)}\n        {...props}\n      >\n        {children}\n      </div>\n    </SidebarContext.Provider>\n  )\n}\n\n// The panel — the well at app scale: a recessed muted surface the active row\n// rises out of as an embossed key. Collapse is a width transition to a slim\n// icon rail (a layout dimension that can't spring cleanly → eased CSS,\n// suppressed under reduced motion); labels fade on the same clock.\nfunction Sidebar({ className, ...props }: React.ComponentProps<\"aside\">) {\n  return (\n    <aside\n      data-slot=\"sidebar\"\n      className={cn(\n        \"flex h-full min-h-0 w-64 shrink-0 flex-col overflow-hidden\",\n        \"border-border/60 bg-muted/40 border-r\",\n        \"transition-[width] duration-300 ease-out motion-reduce:transition-none\",\n        \"group-data-[state=collapsed]/sidebar:w-12\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\n// The label half of a row/group: clipped by the rail, faded on the same clock\n// as the width so collapse never wraps or reflows text.\nfunction SidebarLabel({ className, ...props }: React.ComponentProps<\"span\">) {\n  return (\n    <span\n      data-slot=\"sidebar-label\"\n      className={cn(\n        \"min-w-0 truncate transition-opacity duration-300 motion-reduce:transition-none\",\n        \"group-data-[state=collapsed]/sidebar:opacity-0\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction SidebarHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"sidebar-header\"\n      className={cn(\"flex shrink-0 items-center gap-2 p-3\", className)}\n      {...props}\n    />\n  )\n}\n\nfunction SidebarContent({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"sidebar-content\"\n      className={cn(\"min-h-0 flex-1 overflow-y-auto p-2\", className)}\n      {...props}\n    />\n  )\n}\n\nfunction SidebarFooter({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"sidebar-footer\"\n      className={cn(\n        \"border-border/60 flex shrink-0 items-center gap-2 border-t p-3\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction SidebarGroup({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"sidebar-group\"\n      className={cn(\"flex flex-col gap-0.5 py-2\", className)}\n      {...props}\n    />\n  )\n}\n\n// Group heading with an optional count — the status-grouped sidebar pattern\n// (\"Waiting on you · 2\"). The count stays quiet: a debossed muted chip.\nfunction SidebarGroupLabel({\n  count,\n  className,\n  children,\n  ...props\n}: React.ComponentProps<\"div\"> & { count?: number }) {\n  return (\n    <div\n      data-slot=\"sidebar-group-label\"\n      className={cn(\n        \"text-muted-foreground flex h-6 items-center justify-between gap-2 px-2.5 text-xs font-medium\",\n        className\n      )}\n      {...props}\n    >\n      <SidebarLabel>{children}</SidebarLabel>\n      {count !== undefined && count > 0 ? (\n        <Badge\n          variant=\"muted\"\n          aria-label={`${count} sessions`}\n          className=\"h-4 min-w-4 px-1 text-[10px] tabular-nums transition-opacity duration-300 motion-reduce:transition-none group-data-[state=collapsed]/sidebar:opacity-0\"\n        >\n          {count}\n        </Badge>\n      ) : null}\n    </div>\n  )\n}\n\n// Collapse toggle — a plain seamui Button, usable anywhere inside the provider\n// (sidebar header, workbench header). ⌘/Ctrl+B does the same from anywhere.\nfunction SidebarTrigger({\n  className,\n  onClick,\n  ...props\n}: React.ComponentProps<typeof Button>) {\n  const { open, toggle } = useSidebar()\n  return (\n    <Button\n      data-slot=\"sidebar-trigger\"\n      variant=\"ghost\"\n      size=\"icon\"\n      aria-label={open ? \"Collapse sidebar\" : \"Expand sidebar\"}\n      aria-expanded={open}\n      className={cn(\"size-8\", className)}\n      onClick={(e) => {\n        onClick?.(e)\n        toggle()\n      }}\n      {...props}\n    >\n      <PanelLeft className=\"size-4\" />\n    </Button>\n  )\n}\n\nexport {\n  SidebarProvider,\n  Sidebar,\n  SidebarHeader,\n  SidebarContent,\n  SidebarFooter,\n  SidebarGroup,\n  SidebarGroupLabel,\n  SidebarLabel,\n  SidebarTrigger,\n  useSidebar,\n}\n",
      "type": "registry:ui",
      "target": "components/ui/sidebar.tsx"
    }
  ]
}