{
  "$schema": "https://shadcn-vue.com/schema/registry-item.json",
  "name": "sidebar",
  "title": "Sidebar",
  "type": "registry:ui",
  "files": [
    {
      "path": "packages/registry-vue/components/sidebar/Sidebar.vue",
      "content": "<script setup lang=\"ts\">\nimport type { SidebarProps } from '.'\nimport { cn } from '@/lib/utils'\nimport { Sheet, SheetContent } from '@/components/ui/sheet'\nimport SheetDescription from '@/components/ui/sheet/SheetDescription.vue'\nimport SheetHeader from '@/components/ui/sheet/SheetHeader.vue'\nimport SheetTitle from '@/components/ui/sheet/SheetTitle.vue'\nimport { SIDEBAR_WIDTH_MOBILE, useSidebar } from './utils'\n\ndefineOptions({\n  inheritAttrs: false,\n})\n\nconst props = withDefaults(defineProps<SidebarProps>(), {\n  side: 'left',\n  variant: 'sidebar',\n  collapsible: 'offcanvas',\n})\n\nconst { isMobile, state, openMobile, setOpenMobile } = useSidebar()\n</script>\n\n<template>\n  <div\n    v-if=\"collapsible === 'none'\"\n    data-uipkge\n    data-slot=\"sidebar\"\n    :class=\"cn('bg-sidebar text-sidebar-foreground flex h-full w-(--sidebar-width) flex-col', props.class)\"\n    v-bind=\"$attrs\"\n  >\n    <slot />\n  </div>\n\n  <Sheet v-else-if=\"isMobile\" :open=\"openMobile\" v-bind=\"$attrs\" @update:open=\"setOpenMobile\">\n    <SheetContent\n      data-sidebar=\"sidebar\"\n      data-uipkge\n      data-slot=\"sidebar\"\n      data-mobile=\"true\"\n      :side=\"side\"\n      class=\"bg-sidebar text-sidebar-foreground w-(--sidebar-width) p-0 [&>button]:hidden\"\n      :style=\"{\n        '--sidebar-width': SIDEBAR_WIDTH_MOBILE,\n      }\"\n    >\n      <SheetHeader class=\"sr-only\">\n        <SheetTitle>Sidebar</SheetTitle>\n        <SheetDescription>Displays the mobile sidebar.</SheetDescription>\n      </SheetHeader>\n      <div class=\"flex h-full w-full flex-col\">\n        <slot />\n      </div>\n    </SheetContent>\n  </Sheet>\n\n  <div\n    v-else\n    class=\"group peer text-sidebar-foreground hidden md:block\"\n    data-uipkge\n    data-slot=\"sidebar\"\n    :data-state=\"state\"\n    :data-collapsible=\"state === 'collapsed' ? collapsible : ''\"\n    :data-variant=\"variant\"\n    :data-side=\"side\"\n  >\n    <!-- This is what handles the sidebar gap on desktop  -->\n    <div\n      :class=\"\n        cn(\n          'relative w-(--sidebar-width) bg-transparent transition-[width] duration-200 ease-linear',\n          'group-data-[collapsible=offcanvas]:w-0',\n          'group-data-[side=right]:rotate-180',\n          variant === 'floating' || variant === 'inset'\n            ? 'group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4)))]'\n            : 'group-data-[collapsible=icon]:w-(--sidebar-width-icon)',\n        )\n      \"\n    />\n    <div\n      :class=\"\n        cn(\n          'fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear md:flex',\n          side === 'left'\n            ? 'left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]'\n            : 'right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]',\n          // Adjust the padding for floating and inset variants.\n          variant === 'floating' || variant === 'inset'\n            ? 'p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]'\n            : 'group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l',\n          props.class,\n        )\n      \"\n      v-bind=\"$attrs\"\n    >\n      <div\n        data-sidebar=\"sidebar\"\n        class=\"bg-sidebar group-data-[variant=floating]:border-sidebar-border flex h-full w-full flex-col group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border group-data-[variant=floating]:shadow-sm\"\n      >\n        <slot />\n      </div>\n    </div>\n  </div>\n</template>\n\n<style>\n/* Push any OverlayScroll thumb inside a sidebar 10px in from the right\n   edge so it clears SidebarRail's 8px inside-zone. The CSS variable\n   cascades to every descendant, so consumers wrapping their nav in\n   OverlayScroll (a common pattern when nav items overflow viewport\n   height) get this for free -- no per-call thumb-offset config. */\n[data-slot='sidebar'] {\n  --ovs-thumb-right: 10px;\n}\n</style>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/sidebar/Sidebar.vue"
    },
    {
      "path": "packages/registry-vue/components/sidebar/SidebarContent.vue",
      "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from 'vue'\nimport { cn } from '@/lib/utils'\n\nconst props = defineProps<{\n  class?: HTMLAttributes['class']\n}>()\n</script>\n\n<template>\n  <div\n    data-uipkge\n    data-slot=\"sidebar-content\"\n    data-sidebar=\"content\"\n    :class=\"\n      cn('flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden', props.class)\n    \"\n  >\n    <slot />\n  </div>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/sidebar/SidebarContent.vue"
    },
    {
      "path": "packages/registry-vue/components/sidebar/SidebarFooter.vue",
      "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from 'vue'\nimport { cn } from '@/lib/utils'\n\nconst props = defineProps<{\n  class?: HTMLAttributes['class']\n}>()\n</script>\n\n<template>\n  <div data-uipkge data-slot=\"sidebar-footer\" data-sidebar=\"footer\" :class=\"cn('flex flex-col gap-2 p-2', props.class)\">\n    <slot />\n  </div>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/sidebar/SidebarFooter.vue"
    },
    {
      "path": "packages/registry-vue/components/sidebar/SidebarGroup.vue",
      "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from 'vue'\nimport { cn } from '@/lib/utils'\n\nconst props = defineProps<{\n  class?: HTMLAttributes['class']\n}>()\n</script>\n\n<template>\n  <div\n    data-uipkge\n    data-slot=\"sidebar-group\"\n    data-sidebar=\"group\"\n    :class=\"cn('relative flex w-full min-w-0 flex-col p-2', props.class)\"\n  >\n    <slot />\n  </div>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/sidebar/SidebarGroup.vue"
    },
    {
      "path": "packages/registry-vue/components/sidebar/SidebarGroupAction.vue",
      "content": "<script setup lang=\"ts\">\nimport type { PrimitiveProps } from 'reka-ui'\nimport type { HTMLAttributes } from 'vue'\nimport { Primitive } from 'reka-ui'\nimport { cn } from '@/lib/utils'\n\nconst props = defineProps<\n  PrimitiveProps & {\n    class?: HTMLAttributes['class']\n  }\n>()\n</script>\n\n<template>\n  <Primitive\n    data-uipkge\n    data-slot=\"sidebar-group-action\"\n    data-sidebar=\"group-action\"\n    :as=\"as\"\n    :as-child=\"asChild\"\n    :class=\"\n      cn(\n        'text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground absolute top-3.5 right-3 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-hidden transition-transform focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0',\n        'after:absolute after:-inset-2 md:after:hidden',\n        'group-data-[collapsible=icon]:hidden',\n        props.class,\n      )\n    \"\n  >\n    <slot />\n  </Primitive>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/sidebar/SidebarGroupAction.vue"
    },
    {
      "path": "packages/registry-vue/components/sidebar/SidebarGroupContent.vue",
      "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from 'vue'\nimport { cn } from '@/lib/utils'\n\nconst props = defineProps<{\n  class?: HTMLAttributes['class']\n}>()\n</script>\n\n<template>\n  <div\n    data-uipkge\n    data-slot=\"sidebar-group-content\"\n    data-sidebar=\"group-content\"\n    :class=\"cn('w-full text-sm', props.class)\"\n  >\n    <slot />\n  </div>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/sidebar/SidebarGroupContent.vue"
    },
    {
      "path": "packages/registry-vue/components/sidebar/SidebarGroupLabel.vue",
      "content": "<script setup lang=\"ts\">\nimport type { PrimitiveProps } from 'reka-ui'\nimport type { HTMLAttributes } from 'vue'\nimport { Primitive } from 'reka-ui'\nimport { cn } from '@/lib/utils'\n\nconst props = defineProps<\n  PrimitiveProps & {\n    class?: HTMLAttributes['class']\n  }\n>()\n</script>\n\n<template>\n  <Primitive\n    data-uipkge\n    data-slot=\"sidebar-group-label\"\n    data-sidebar=\"group-label\"\n    :as=\"as\"\n    :as-child=\"asChild\"\n    :class=\"\n      cn(\n        'text-sidebar-foreground/70 ring-sidebar-ring mt-2 mb-1 flex h-8 shrink-0 items-center rounded-md px-2 text-[13px] font-medium outline-hidden transition-[margin,opacity] duration-200 ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0',\n        'group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0',\n        props.class,\n      )\n    \"\n  >\n    <slot />\n  </Primitive>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/sidebar/SidebarGroupLabel.vue"
    },
    {
      "path": "packages/registry-vue/components/sidebar/SidebarHeader.vue",
      "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from 'vue'\nimport { cn } from '@/lib/utils'\n\nconst props = defineProps<{\n  class?: HTMLAttributes['class']\n}>()\n</script>\n\n<template>\n  <div data-uipkge data-slot=\"sidebar-header\" data-sidebar=\"header\" :class=\"cn('flex flex-col gap-2 p-2', props.class)\">\n    <slot />\n  </div>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/sidebar/SidebarHeader.vue"
    },
    {
      "path": "packages/registry-vue/components/sidebar/SidebarInput.vue",
      "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from 'vue'\nimport { cn } from '@/lib/utils'\nimport { Input } from '@/components/ui/input'\n\nconst props = defineProps<{\n  class?: HTMLAttributes['class']\n}>()\n</script>\n\n<template>\n  <!-- Sidebar-tuned wrapper over the kit's <Input>. Forwards every slot so\n       consumers can pass prefix (search icon) / suffix (kbd hint) / default\n       slot content without losing the sidebar height + bg defaults. -->\n  <Input\n    data-uipkge\n    data-slot=\"sidebar-input\"\n    data-sidebar=\"input\"\n    :class=\"cn('bg-background h-8 w-full shadow-none', props.class)\"\n  >\n    <template v-for=\"(_, name) in $slots\" #[name]=\"slotProps\">\n      <slot :name=\"name\" v-bind=\"slotProps\" />\n    </template>\n  </Input>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/sidebar/SidebarInput.vue"
    },
    {
      "path": "packages/registry-vue/components/sidebar/SidebarInset.vue",
      "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from 'vue'\nimport { cn } from '@/lib/utils'\n\nconst props = defineProps<{\n  class?: HTMLAttributes['class']\n}>()\n</script>\n\n<template>\n  <main\n    data-uipkge\n    data-slot=\"sidebar-inset\"\n    :class=\"\n      cn(\n        // min-w-0 is load-bearing: a flex-1 child without it inherits min-width: auto,\n        // which means any single wide descendant (chart, table, code block) blows the\n        // inset's width past its grid track. The upstream shadcn-ui sidebar omits this.\n        'bg-background relative flex w-full min-w-0 flex-1 flex-col',\n        'md:peer-data-[variant=inset]:m-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow-sm md:peer-data-[variant=inset]:peer-data-[state=collapsed]:ml-2',\n        props.class,\n      )\n    \"\n  >\n    <slot />\n  </main>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/sidebar/SidebarInset.vue"
    },
    {
      "path": "packages/registry-vue/components/sidebar/SidebarMenu.vue",
      "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from 'vue'\nimport { cn } from '@/lib/utils'\n\nconst props = defineProps<{\n  class?: HTMLAttributes['class']\n}>()\n</script>\n\n<template>\n  <ul\n    data-uipkge\n    data-slot=\"sidebar-menu\"\n    data-sidebar=\"menu\"\n    :class=\"cn('flex w-full min-w-0 flex-col gap-1', props.class)\"\n  >\n    <slot />\n  </ul>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/sidebar/SidebarMenu.vue"
    },
    {
      "path": "packages/registry-vue/components/sidebar/SidebarMenuAction.vue",
      "content": "<script setup lang=\"ts\">\nimport type { PrimitiveProps } from 'reka-ui'\nimport type { HTMLAttributes } from 'vue'\nimport { Primitive } from 'reka-ui'\nimport { cn } from '@/lib/utils'\n\nconst props = withDefaults(\n  defineProps<\n    PrimitiveProps & {\n      showOnHover?: boolean\n      class?: HTMLAttributes['class']\n    }\n  >(),\n  {\n    as: 'button',\n  },\n)\n</script>\n\n<template>\n  <Primitive\n    data-uipkge\n    data-slot=\"sidebar-menu-action\"\n    data-sidebar=\"menu-action\"\n    :class=\"\n      cn(\n        'text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground peer-hover/menu-button:text-sidebar-accent-foreground absolute top-1.5 right-1 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-hidden transition-transform focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0',\n        'after:absolute after:-inset-2 md:after:hidden',\n        'peer-data-[size=sm]/menu-button:top-1',\n        'peer-data-[size=default]/menu-button:top-1.5',\n        'peer-data-[size=lg]/menu-button:top-2.5',\n        'group-data-[collapsible=icon]:hidden',\n        showOnHover &&\n          'peer-data-[active=true]/menu-button:text-sidebar-accent-foreground group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 md:opacity-0',\n        props.class,\n      )\n    \"\n    :as=\"as\"\n    :as-child=\"asChild\"\n  >\n    <slot />\n  </Primitive>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/sidebar/SidebarMenuAction.vue"
    },
    {
      "path": "packages/registry-vue/components/sidebar/SidebarMenuBadge.vue",
      "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from 'vue'\nimport { cn } from '@/lib/utils'\n\nconst props = defineProps<{\n  class?: HTMLAttributes['class']\n}>()\n</script>\n\n<template>\n  <div\n    data-uipkge\n    data-slot=\"sidebar-menu-badge\"\n    data-sidebar=\"menu-badge\"\n    :class=\"\n      cn(\n        'text-sidebar-foreground pointer-events-none absolute right-1 flex h-5 min-w-5 items-center justify-center rounded-md px-1 text-xs font-medium tabular-nums select-none',\n        'peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[active=true]/menu-button:text-sidebar-accent-foreground',\n        'peer-data-[size=sm]/menu-button:top-1',\n        'peer-data-[size=default]/menu-button:top-1.5',\n        'peer-data-[size=lg]/menu-button:top-2.5',\n        'group-data-[collapsible=icon]:hidden',\n        props.class,\n      )\n    \"\n  >\n    <slot />\n  </div>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/sidebar/SidebarMenuBadge.vue"
    },
    {
      "path": "packages/registry-vue/components/sidebar/SidebarMenuButton.vue",
      "content": "<script setup lang=\"ts\">\nimport type { Component } from 'vue'\nimport type { SidebarMenuButtonProps } from './SidebarMenuButtonChild.vue'\nimport { reactiveOmit } from '@vueuse/core'\nimport { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'\nimport SidebarMenuButtonChild from './SidebarMenuButtonChild.vue'\nimport { useSidebar } from './utils'\n\ndefineOptions({\n  inheritAttrs: false,\n})\n\nconst props = withDefaults(\n  defineProps<\n    SidebarMenuButtonProps & {\n      tooltip?: string | Component\n    }\n  >(),\n  {\n    as: 'button',\n    variant: 'default',\n    size: 'default',\n  },\n)\n\nconst { isMobile, state } = useSidebar()\n\nconst delegatedProps = reactiveOmit(props, 'tooltip')\n</script>\n\n<template>\n  <SidebarMenuButtonChild v-if=\"!tooltip\" v-bind=\"{ ...delegatedProps, ...$attrs }\">\n    <slot />\n  </SidebarMenuButtonChild>\n\n  <Tooltip v-else>\n    <TooltipTrigger as-child>\n      <SidebarMenuButtonChild v-bind=\"{ ...delegatedProps, ...$attrs }\">\n        <slot />\n      </SidebarMenuButtonChild>\n    </TooltipTrigger>\n    <TooltipContent side=\"right\" align=\"center\" :hidden=\"state !== 'collapsed' || isMobile\">\n      <template v-if=\"typeof tooltip === 'string'\">\n        {{ tooltip }}\n      </template>\n      <component :is=\"tooltip\" v-else />\n    </TooltipContent>\n  </Tooltip>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/sidebar/SidebarMenuButton.vue"
    },
    {
      "path": "packages/registry-vue/components/sidebar/SidebarMenuButtonChild.vue",
      "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from 'vue'\nimport { Primitive } from 'reka-ui'\nimport { cn } from '@/lib/utils'\nimport { sidebarMenuButtonVariants } from './sidebar.variants'\n\n// Inline `as`/`asChild` from PrimitiveProps -- see Button.vue for why\n// `extends /* @vue-ignore */ PrimitiveProps` was wrong: the annotation\n// drops every interface field from runtime props, not just the\n// extended ones, so variant/size/isActive silently become attrs.\n//\n// Same goes for variant/size: the SFC compiler can't extract them\n// from `SidebarMenuButtonVariants['variant']` indexed-access types,\n// so the unions are inlined explicitly.\nexport interface SidebarMenuButtonProps {\n  as?: string\n  asChild?: boolean\n  variant?: 'default' | 'outline'\n  size?: 'default' | 'sm' | 'lg'\n  isActive?: boolean\n  class?: HTMLAttributes['class']\n}\n\nconst props = withDefaults(defineProps<SidebarMenuButtonProps>(), {\n  as: 'button',\n  variant: 'default',\n  size: 'default',\n})\n</script>\n\n<template>\n  <Primitive\n    data-uipkge\n    data-slot=\"sidebar-menu-button\"\n    data-sidebar=\"menu-button\"\n    :data-size=\"size\"\n    :data-active=\"isActive\"\n    :class=\"cn(sidebarMenuButtonVariants({ variant, size }), props.class)\"\n    :as=\"as\"\n    :as-child=\"asChild\"\n    v-bind=\"$attrs\"\n  >\n    <slot />\n  </Primitive>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/sidebar/SidebarMenuButtonChild.vue"
    },
    {
      "path": "packages/registry-vue/components/sidebar/SidebarMenuItem.vue",
      "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from 'vue'\nimport { cn } from '@/lib/utils'\n\nconst props = defineProps<{\n  class?: HTMLAttributes['class']\n}>()\n</script>\n\n<template>\n  <li\n    data-uipkge\n    data-slot=\"sidebar-menu-item\"\n    data-sidebar=\"menu-item\"\n    :class=\"cn('group/menu-item relative', props.class)\"\n  >\n    <slot />\n  </li>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/sidebar/SidebarMenuItem.vue"
    },
    {
      "path": "packages/registry-vue/components/sidebar/SidebarMenuSkeleton.vue",
      "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from 'vue'\nimport { computed } from 'vue'\nimport { cn } from '@/lib/utils'\nimport { Skeleton } from '@/components/ui/skeleton'\n\nconst props = defineProps<{\n  showIcon?: boolean\n  class?: HTMLAttributes['class']\n}>()\n\nconst width = computed(() => {\n  return `${Math.floor(Math.random() * 40) + 50}%`\n})\n</script>\n\n<template>\n  <div\n    data-uipkge\n    data-slot=\"sidebar-menu-skeleton\"\n    data-sidebar=\"menu-skeleton\"\n    :class=\"cn('flex h-8 items-center gap-2 rounded-md px-2', props.class)\"\n  >\n    <Skeleton v-if=\"showIcon\" class=\"size-4 rounded-md\" data-sidebar=\"menu-skeleton-icon\" />\n\n    <Skeleton\n      class=\"h-4 max-w-(--skeleton-width) flex-1\"\n      data-sidebar=\"menu-skeleton-text\"\n      :style=\"{ '--skeleton-width': width }\"\n    />\n  </div>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/sidebar/SidebarMenuSkeleton.vue"
    },
    {
      "path": "packages/registry-vue/components/sidebar/SidebarMenuSub.vue",
      "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from 'vue'\nimport { cn } from '@/lib/utils'\n\nconst props = defineProps<{\n  class?: HTMLAttributes['class']\n}>()\n</script>\n\n<template>\n  <ul\n    data-uipkge\n    data-slot=\"sidebar-menu-sub\"\n    data-sidebar=\"menu-badge\"\n    :class=\"\n      cn(\n        'border-sidebar-border mx-3.5 flex min-w-0 translate-x-px flex-col gap-1 border-l px-2.5 py-0.5',\n        'group-data-[collapsible=icon]:hidden',\n        props.class,\n      )\n    \"\n  >\n    <slot />\n  </ul>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/sidebar/SidebarMenuSub.vue"
    },
    {
      "path": "packages/registry-vue/components/sidebar/SidebarMenuSubButton.vue",
      "content": "<script setup lang=\"ts\">\nimport type { PrimitiveProps } from 'reka-ui'\nimport type { HTMLAttributes } from 'vue'\nimport { Primitive } from 'reka-ui'\nimport { cn } from '@/lib/utils'\n\nconst props = withDefaults(\n  defineProps<\n    PrimitiveProps & {\n      size?: 'sm' | 'md'\n      isActive?: boolean\n      class?: HTMLAttributes['class']\n    }\n  >(),\n  {\n    as: 'a',\n    size: 'md',\n  },\n)\n</script>\n\n<template>\n  <Primitive\n    data-uipkge\n    data-slot=\"sidebar-menu-sub-button\"\n    data-sidebar=\"menu-sub-button\"\n    :as=\"as\"\n    :as-child=\"asChild\"\n    :data-size=\"size\"\n    :data-active=\"isActive\"\n    :class=\"\n      cn(\n        'text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground [&>svg]:text-sidebar-accent-foreground flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 outline-hidden focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0',\n        'data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground',\n        size === 'sm' && 'text-xs',\n        size === 'md' && 'text-sm',\n        'group-data-[collapsible=icon]:hidden',\n        props.class,\n      )\n    \"\n  >\n    <slot />\n  </Primitive>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/sidebar/SidebarMenuSubButton.vue"
    },
    {
      "path": "packages/registry-vue/components/sidebar/SidebarMenuSubItem.vue",
      "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from 'vue'\nimport { cn } from '@/lib/utils'\n\nconst props = defineProps<{\n  class?: HTMLAttributes['class']\n}>()\n</script>\n\n<template>\n  <li\n    data-uipkge\n    data-slot=\"sidebar-menu-sub-item\"\n    data-sidebar=\"menu-sub-item\"\n    :class=\"cn('group/menu-sub-item relative', props.class)\"\n  >\n    <slot />\n  </li>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/sidebar/SidebarMenuSubItem.vue"
    },
    {
      "path": "packages/registry-vue/components/sidebar/SidebarProvider.vue",
      "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from 'vue'\nimport { useEventListener, useMediaQuery } from '@vueuse/core'\nimport { TooltipProvider } from 'reka-ui'\nimport { computed, onMounted, ref } from 'vue'\nimport { cn } from '@/lib/utils'\nimport {\n  provideSidebarContext,\n  SIDEBAR_COOKIE_MAX_AGE,\n  SIDEBAR_COOKIE_NAME,\n  SIDEBAR_KEYBOARD_SHORTCUT,\n  SIDEBAR_WIDTH,\n  SIDEBAR_WIDTH_ICON,\n} from './utils'\n\nconst props = defineProps<{\n  defaultOpen?: boolean\n  open?: boolean\n  class?: HTMLAttributes['class']\n}>()\n\nconst emits = defineEmits<{\n  'update:open': [open: boolean]\n}>()\n\n// `useMediaQuery` returns `false` during SSR (no matchMedia) and\n// re-evaluates synchronously on the client's first render. If the\n// viewport is < 768px, SSR HTML has the desktop branch but the\n// client wants the mobile <Sheet> branch -- Vue throws a hydration\n// mismatch and the Sheet renders without its overlay (the desktop\n// sidebar's div is the parent the diff lands against).\n//\n// Gating on `mounted` makes both the server and the client's first\n// synchronous render produce the desktop branch unconditionally;\n// `onMounted` then flips the flag and `Sidebar.vue`'s v-else-if\n// re-runs against the real matchMedia signal. Cost: a brief\n// desktop-layout flash for mobile users on first paint. That's the\n// universal tradeoff for SSR-without-viewport-detection -- there is\n// no clean way to know the viewport on the server.\nconst mediaMobile = useMediaQuery('(max-width: 768px)')\nconst mounted = ref(false)\nonMounted(() => {\n  mounted.value = true\n})\nconst isMobile = computed(() => mounted.value && mediaMobile.value)\nconst openMobile = ref(false)\n\n// useVModel with passive:true returns a ref that appears writable but\n// doesn't actually update when no parent v-model is bound. Fall back to\n// a plain ref so toggleSidebar / setOpen work reliably.\nconst open = ref<boolean>(true)\n\nfunction setOpen(value: boolean) {\n  open.value = value // emits('update:open', value)\n\n  // This sets the cookie to keep the sidebar state.\n  document.cookie = `${SIDEBAR_COOKIE_NAME}=${open.value}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`\n}\n\nfunction setOpenMobile(value: boolean) {\n  openMobile.value = value\n}\n\n// Helper to toggle the sidebar.\nfunction toggleSidebar() {\n  return isMobile.value ? setOpenMobile(!openMobile.value) : setOpen(!open.value)\n}\n\nuseEventListener('keydown', (event: KeyboardEvent) => {\n  if (event.key === SIDEBAR_KEYBOARD_SHORTCUT && (event.metaKey || event.ctrlKey)) {\n    event.preventDefault()\n    toggleSidebar()\n  }\n})\n\n// We add a state so that we can do data-state=\"expanded\" or \"collapsed\".\n// This makes it easier to style the sidebar with Tailwind classes.\nconst state = computed(() => (open.value ? 'expanded' : 'collapsed'))\n\nprovideSidebarContext({\n  state,\n  open,\n  setOpen,\n  isMobile,\n  openMobile,\n  setOpenMobile,\n  toggleSidebar,\n})\n</script>\n\n<template>\n  <TooltipProvider :delay-duration=\"0\">\n    <div\n      data-uipkge\n      data-slot=\"sidebar-wrapper\"\n      :style=\"{\n        '--sidebar-width': SIDEBAR_WIDTH,\n        '--sidebar-width-icon': SIDEBAR_WIDTH_ICON,\n      }\"\n      :class=\"cn('group/sidebar-wrapper has-data-[variant=inset]:bg-sidebar flex min-h-svh w-full', props.class)\"\n      v-bind=\"$attrs\"\n    >\n      <slot />\n    </div>\n  </TooltipProvider>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/sidebar/SidebarProvider.vue"
    },
    {
      "path": "packages/registry-vue/components/sidebar/SidebarRail.vue",
      "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from 'vue'\nimport { cn } from '@/lib/utils'\nimport { useSidebar } from './utils'\n\nconst props = defineProps<{\n  class?: HTMLAttributes['class']\n}>()\n\nconst { toggleSidebar } = useSidebar()\n</script>\n\n<template>\n  <button\n    type=\"button\"\n    data-sidebar=\"rail\"\n    data-uipkge\n    data-slot=\"sidebar-rail\"\n    aria-label=\"Toggle Sidebar\"\n    :tabindex=\"-1\"\n    title=\"Toggle Sidebar\"\n    :class=\"\n      cn(\n        'hover:after:bg-sidebar-border focus-visible:ring-ring absolute inset-y-0 z-20 hidden w-4 -translate-x-1/2 transition-colors duration-200 ease-linear group-data-[side=left]:-right-4 group-data-[side=right]:left-0 after:absolute after:inset-y-0 after:left-1/2 after:w-[2px] focus-visible:ring-2 focus-visible:outline-none sm:flex',\n        'in-data-[side=left]:cursor-w-resize in-data-[side=right]:cursor-e-resize',\n        '[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize',\n        'hover:group-data-[collapsible=offcanvas]:bg-sidebar group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full',\n        '[[data-side=left][data-collapsible=offcanvas]_&]:-right-2',\n        '[[data-side=right][data-collapsible=offcanvas]_&]:-left-2',\n        props.class,\n      )\n    \"\n    @click=\"toggleSidebar\"\n  >\n    <slot />\n  </button>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/sidebar/SidebarRail.vue"
    },
    {
      "path": "packages/registry-vue/components/sidebar/SidebarSeparator.vue",
      "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from 'vue'\nimport { cn } from '@/lib/utils'\nimport { Separator } from '@/components/ui/separator'\n\nconst props = defineProps<{\n  class?: HTMLAttributes['class']\n}>()\n</script>\n\n<template>\n  <Separator\n    data-uipkge\n    data-slot=\"sidebar-separator\"\n    data-sidebar=\"separator\"\n    :class=\"cn('bg-sidebar-border mx-2 w-auto', props.class)\"\n  >\n    <slot />\n  </Separator>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/sidebar/SidebarSeparator.vue"
    },
    {
      "path": "packages/registry-vue/components/sidebar/SidebarTrigger.vue",
      "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from 'vue'\nimport { PanelLeft } from 'lucide-vue-next'\nimport { cn } from '@/lib/utils'\nimport { Button } from '@/components/ui/button'\nimport { useSidebar } from './utils'\n\nconst props = defineProps<{\n  class?: HTMLAttributes['class']\n}>()\n\nconst { toggleSidebar } = useSidebar()\n</script>\n\n<template>\n  <Button\n    data-sidebar=\"trigger\"\n    data-uipkge\n    data-slot=\"sidebar-trigger\"\n    variant=\"ghost\"\n    size=\"icon\"\n    :class=\"cn('focus-visible:ring-ring h-7 w-7 focus-visible:ring-2 focus-visible:outline-none', props.class)\"\n    @click=\"toggleSidebar\"\n  >\n    <PanelLeft />\n    <span class=\"sr-only\">Toggle Sidebar</span>\n  </Button>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/sidebar/SidebarTrigger.vue"
    },
    {
      "path": "packages/registry-vue/components/sidebar/sidebar.variants.ts",
      "content": "import type { VariantProps } from 'class-variance-authority'\nimport { cva } from 'class-variance-authority'\n\n/**\n * Variant definitions live in their own file (rather than the package\n * `index.ts`) so consuming Vue SFCs can import without creating a circular\n * dependency through the index. See card.variants.ts for the canonical\n * example + the SSR symptom that motivated the split.\n */\n\nexport const sidebarMenuButtonVariants = cva(\n  'peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm text-muted-foreground outline-hidden ring-sidebar-ring transition-[width,height,padding,color,background-color] hover:bg-sidebar-accent/50 hover:text-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-foreground disabled:pointer-events-none disabled:opacity-50 group-has-data-[sidebar=menu-action]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-primary/10 data-[active=true]:font-medium data-[active=true]:text-primary data-[state=open]:hover:bg-sidebar-accent/50 data-[state=open]:hover:text-foreground group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0',\n  {\n    variants: {\n      variant: {\n        default: 'hover:bg-sidebar-accent hover:text-foreground',\n        outline:\n          'bg-background shadow-[0_0_0_1px_var(--sidebar-border)] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_var(--sidebar-accent)]',\n      },\n      size: {\n        default: 'h-11 text-sm',\n        sm: 'h-10 text-xs',\n        lg: 'h-12 text-sm group-data-[collapsible=icon]:p-0!',\n      },\n    },\n    defaultVariants: {\n      variant: 'default',\n      size: 'default',\n    },\n  },\n)\n\nexport type SidebarMenuButtonVariants = VariantProps<typeof sidebarMenuButtonVariants>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/sidebar/sidebar.variants.ts"
    },
    {
      "path": "packages/registry-vue/components/sidebar/index.ts",
      "content": "import type { HTMLAttributes } from 'vue'\nexport interface SidebarProps {\n  side?: 'left' | 'right'\n  variant?: 'sidebar' | 'floating' | 'inset'\n  collapsible?: 'offcanvas' | 'icon' | 'none'\n  class?: HTMLAttributes['class']\n}\n\nexport { default as Sidebar } from './Sidebar.vue'\nexport { default as SidebarContent } from './SidebarContent.vue'\nexport { default as SidebarFooter } from './SidebarFooter.vue'\nexport { default as SidebarGroup } from './SidebarGroup.vue'\nexport { default as SidebarGroupAction } from './SidebarGroupAction.vue'\nexport { default as SidebarGroupContent } from './SidebarGroupContent.vue'\nexport { default as SidebarGroupLabel } from './SidebarGroupLabel.vue'\nexport { default as SidebarHeader } from './SidebarHeader.vue'\nexport { default as SidebarInput } from './SidebarInput.vue'\nexport { default as SidebarInset } from './SidebarInset.vue'\nexport { default as SidebarMenu } from './SidebarMenu.vue'\nexport { default as SidebarMenuAction } from './SidebarMenuAction.vue'\nexport { default as SidebarMenuBadge } from './SidebarMenuBadge.vue'\nexport { default as SidebarMenuButton } from './SidebarMenuButton.vue'\nexport { default as SidebarMenuItem } from './SidebarMenuItem.vue'\nexport { default as SidebarMenuSkeleton } from './SidebarMenuSkeleton.vue'\nexport { default as SidebarMenuSub } from './SidebarMenuSub.vue'\nexport { default as SidebarMenuSubButton } from './SidebarMenuSubButton.vue'\nexport { default as SidebarMenuSubItem } from './SidebarMenuSubItem.vue'\nexport { default as SidebarProvider } from './SidebarProvider.vue'\nexport { default as SidebarRail } from './SidebarRail.vue'\nexport { default as SidebarSeparator } from './SidebarSeparator.vue'\nexport { default as SidebarTrigger } from './SidebarTrigger.vue'\n\nexport { useSidebar } from './utils'\n\n// Re-export variant API from the sibling file (kept separate to avoid the\n// Component.vue <-> index.ts circular import that broke dev SSR for Card).\nexport { sidebarMenuButtonVariants, type SidebarMenuButtonVariants } from './sidebar.variants'\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/sidebar/index.ts"
    },
    {
      "path": "packages/registry-vue/components/sidebar/utils.ts",
      "content": "import type { ComputedRef, Ref } from 'vue'\nimport { createContext } from 'reka-ui'\n\nexport const SIDEBAR_COOKIE_NAME = 'sidebar_state'\nexport const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7\nexport const SIDEBAR_WIDTH = '16rem'\nexport const SIDEBAR_WIDTH_MOBILE = '18rem'\nexport const SIDEBAR_WIDTH_ICON = '3rem'\nexport const SIDEBAR_KEYBOARD_SHORTCUT = 'b'\n\nexport const [useSidebar, provideSidebarContext] = createContext<{\n  state: ComputedRef<'expanded' | 'collapsed'>\n  open: Ref<boolean>\n  setOpen: (value: boolean) => void\n  isMobile: Ref<boolean>\n  openMobile: Ref<boolean>\n  setOpenMobile: (value: boolean) => void\n  toggleSidebar: () => void\n}>('Sidebar')\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/sidebar/utils.ts"
    }
  ],
  "dependencies": [
    "@vueuse/core",
    "class-variance-authority",
    "lucide-vue-next",
    "reka-ui"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/vue/button.json",
    "https://uipkge.dev/r/vue/input.json",
    "https://uipkge.dev/r/vue/separator.json",
    "https://uipkge.dev/r/vue/sheet.json",
    "https://uipkge.dev/r/vue/skeleton.json",
    "https://uipkge.dev/r/vue/tooltip.json"
  ],
  "description": "Full-height app sidebar — collapsible to icons, with grouping, sub-grouping, and integrated search. The navigation surface for product apps with many sections.",
  "categories": [
    "navigation"
  ]
}