{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "select",
  "title": "Select",
  "description": "Accessible selection with Kobalte keyboard navigation.",
  "dependencies": [
    "solid-js@^1.9.15",
    "@kobalte/core@https://github.com/jhomra21/kobalte/releases/download/kobalte-solid1-audiocn-e0e3bf095f05c7e61230a251b79181c6d834d408/kobalte-core-0.13.14-audiocn.0.e0e3bf09.tgz"
  ],
  "registryDependencies": [
    "@audiocn-solid/core"
  ],
  "files": [
    {
      "path": "components/ui/select.tsx",
      "content": "import * as SelectPrimitive from \"@kobalte/core/select\";\nimport type {\n  SelectContentCommonProps,\n  SelectContentOptions,\n  SelectTriggerCommonProps,\n} from \"@kobalte/core/select\";\nimport { children, createSignal, onCleanup } from \"solid-js\";\n\nimport { createCompatEffect } from \"@/lib/solid/effect\";\nimport type { ButtonDOMProps, DivDOMProps } from \"@/lib/solid/jsx-types\";\nimport { omitProps } from \"@/lib/solid/props\";\nimport { setRefValue } from \"@/lib/solid/ref\";\nimport type { RefTarget } from \"@/lib/solid/ref\";\nimport { mergeStyleVars } from \"@/lib/solid/style\";\nimport type { StyleValue } from \"@/lib/solid/style\";\nimport { cn } from \"@/lib/utils\";\n\nexport const Select = SelectPrimitive.Root;\n\nexport const SelectItem = SelectPrimitive.Item;\n\nexport const SelectGroup = SelectPrimitive.Section;\n\nexport const SelectValue = SelectPrimitive.Value;\n\nexport const SelectLabel = (\n  props: Parameters<typeof SelectPrimitive.Section>[0] & { className?: string }\n) => (\n  <SelectPrimitive.Section\n    data-slot=\"select-label\"\n    class={cn(\n      \"text-muted-foreground px-2 py-1 text-xs\",\n      props.class,\n      props.className\n    )}\n    {...omitProps(props, [\"class\", \"className\"])}\n  />\n);\n\nexport const SelectSeparator = (\n  props: DivDOMProps & { className?: string }\n) => (\n  <div\n    role=\"separator\"\n    data-slot=\"select-separator\"\n    class={cn(\n      \"bg-border pointer-events-none -mx-1 my-1 h-px\",\n      props.class,\n      props.className\n    )}\n    {...omitProps(props, [\"class\", \"className\"])}\n  />\n);\n\ntype SelectScrollButtonProps = Omit<\n  DivDOMProps,\n  | \"ref\"\n  | \"style\"\n  | \"onPointerEnter\"\n  | \"onPointerLeave\"\n  | \"onPointerDown\"\n  | \"onPointerUp\"\n  | \"onPointerCancel\"\n> & {\n  className?: string;\n  ref?: RefTarget<HTMLDivElement>;\n  style?: StyleValue;\n};\n\n// Kobalte scrolls its listbox rather than the popup and has no arrow primitive.\nconst SelectScrollButton = (\n  props: SelectScrollButtonProps & { direction: \"up\" | \"down\" }\n) => {\n  const select = SelectPrimitive.useSelectContext();\n  const [element, setElement] = createSignal<HTMLDivElement>();\n  const [visible, setVisible] = createSignal(false);\n  let listbox: HTMLElement | undefined;\n  let timer: ReturnType<typeof setInterval> | undefined;\n  const stop = () => clearInterval(timer);\n\n  const scroll = () =>\n    listbox?.scrollBy(0, props.direction === \"up\" ? -24 : 24);\n\n  const start = () => {\n    stop();\n    scroll();\n    timer = setInterval(scroll, 50);\n  };\n\n  onCleanup(stop);\n\n  createCompatEffect(\n    () => ({ element: element(), open: select.isOpen() }),\n    ({ element, open }) => {\n      if (!element || !open) return;\n      listbox =\n        element.parentElement?.querySelector<HTMLElement>('[role=\"listbox\"]') ??\n        undefined;\n\n      if (!listbox) return;\n      const target = listbox;\n\n      const update = () => {\n        const remaining =\n          target.scrollHeight - target.clientHeight - target.scrollTop;\n\n        setVisible(\n          props.direction === \"up\" ? target.scrollTop > 1 : remaining > 1\n        );\n      };\n\n      const observer = new ResizeObserver(update);\n      observer.observe(target);\n      target.addEventListener(\"scroll\", update);\n      update();\n\n      return () => {\n        stop();\n        observer.disconnect();\n        target.removeEventListener(\"scroll\", update);\n        listbox = undefined;\n      };\n    }\n  );\n\n  return (\n    <div\n      aria-hidden=\"true\"\n      data-slot={`select-scroll-${props.direction}-button`}\n      hidden={!visible()}\n      class={cn(\n        \"bg-popover absolute z-10 w-full cursor-default items-center justify-center py-1 [&_svg]:size-4\",\n        props.direction === \"up\" ? \"top-0\" : \"bottom-0\",\n        props.class,\n        props.className\n      )}\n      style={mergeStyleVars(props.style, {\n        display: visible() ? \"flex\" : \"none\",\n      })}\n      ref={(node) => {\n        setElement(node);\n        setRefValue(props.ref, node);\n      }}\n      onPointerEnter={start}\n      onPointerLeave={stop}\n      onPointerDown={(event: PointerEvent) => {\n        event.preventDefault();\n        start();\n      }}\n      onPointerUp={stop}\n      onPointerCancel={stop}\n      {...omitProps(props, [\n        \"class\",\n        \"className\",\n        \"children\",\n        \"direction\",\n        \"ref\",\n        \"style\",\n      ])}\n    >\n      <svg fill=\"currentColor\" viewBox=\"0 0 256 256\">\n        {/* Regular Phosphor Carets, MIT © Phosphor Icons. */}\n        <path\n          d={\n            props.direction === \"up\"\n              ? \"M213.66,165.66a8,8,0,0,1-11.32,0L128,91.31,53.66,165.66a8,8,0,0,1-11.32-11.32l80-80a8,8,0,0,1,11.32,0l80,80A8,8,0,0,1,213.66,165.66Z\"\n              : \"M213.66,101.66l-80,80a8,8,0,0,1-11.32,0l-80-80A8,8,0,0,1,53.66,90.34L128,164.69l74.34-74.35a8,8,0,0,1,11.32,11.32Z\"\n          }\n        />\n      </svg>\n    </div>\n  );\n};\n\nexport const SelectScrollUpButton = (props: SelectScrollButtonProps) => (\n  <SelectScrollButton {...props} direction=\"up\" />\n);\n\nexport const SelectScrollDownButton = (props: SelectScrollButtonProps) => (\n  <SelectScrollButton {...props} direction=\"down\" />\n);\n\nexport interface SelectTriggerProps\n  extends\n    Omit<ButtonDOMProps, keyof SelectTriggerCommonProps | \"style\">,\n    Partial<SelectTriggerCommonProps<HTMLButtonElement>> {\n  style?: SelectContentCommonProps[\"style\"];\n  className?: string;\n  size?: \"default\" | \"sm\";\n}\n\nexport const SelectTrigger = (props: SelectTriggerProps) => {\n  const rest = omitProps(props, [\"class\", \"className\", \"children\", \"size\"]);\n\n  // Own the content before Kobalte's polymorphic button so SSR and hydration\n  // evaluate it under the same owner, rather than different internal branches.\n  const content = children(() => (\n    <>\n      {props.children}\n      <SelectPrimitive.Icon aria-hidden=\"true\">\n        <svg\n          class=\"text-muted-foreground size-4 shrink-0\"\n          fill=\"currentColor\"\n          viewBox=\"0 0 256 256\"\n        >\n          {/* Regular Phosphor CaretDown, MIT © Phosphor Icons. */}\n          <path d=\"M213.66,101.66l-80,80a8,8,0,0,1-11.32,0l-80-80A8,8,0,0,1,53.66,90.34L128,164.69l74.34-74.35a8,8,0,0,1,11.32,11.32Z\" />\n        </svg>\n      </SelectPrimitive.Icon>\n    </>\n  ));\n\n  return (\n    <SelectPrimitive.Trigger\n      role=\"combobox\"\n      data-size={props.size ?? \"default\"}\n      class={cn(\n        \"bg-input/50 focus-visible:border-ring focus-visible:ring-ring/30 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-placeholder:text-muted-foreground dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 flex items-center justify-between gap-1.5 rounded-2xl border border-transparent px-3 py-2 text-sm whitespace-nowrap transition-[color,box-shadow] duration-200 outline-none focus-visible:ring-3 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:ring-3 data-[size=default]:h-8 data-[size=sm]:h-7 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-1.5 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n        props.class,\n        props.className\n      )}\n      data-slot=\"select-trigger\"\n      {...rest}\n    >\n      {content()}\n    </SelectPrimitive.Trigger>\n  );\n};\n\nexport interface SelectContentProps\n  extends Omit<DivDOMProps, \"id\" | \"style\">, SelectContentOptions {\n  id?: string;\n  style?: SelectContentCommonProps[\"style\"];\n  className?: string;\n}\n\nexport const SelectContent = (props: SelectContentProps) => {\n  const rest = omitProps(props, [\"class\", \"className\", \"ref\"]);\n  const [content, setContent] = createSignal<HTMLDivElement>();\n  const select = SelectPrimitive.useSelectContext();\n\n  createCompatEffect(\n    () => (select.isOpen() ? content() : undefined),\n    (element) => {\n      if (!element) return;\n\n      // Floating UI constrains the popup after Kobalte's initial option focus.\n      // Once that size settles, reveal the focused option in the listbox again.\n      const observer = new ResizeObserver(() => {\n        const focused = element.querySelector<HTMLElement>(\n          '[role=\"option\"][data-highlighted]'\n        );\n\n        if (focused) {\n          const listbox = focused.closest<HTMLElement>('[role=\"listbox\"]');\n\n          if (!listbox) return;\n          const itemRect = focused.getBoundingClientRect();\n          const listRect = listbox.getBoundingClientRect();\n\n          if (itemRect.top < listRect.top) {\n            listbox.scrollTop += itemRect.top - listRect.top;\n          } else if (itemRect.bottom > listRect.bottom) {\n            listbox.scrollTop += itemRect.bottom - listRect.bottom;\n          }\n        }\n      });\n\n      observer.observe(element);\n\n      return () => observer.disconnect();\n    }\n  );\n\n  return (\n    <SelectPrimitive.Portal>\n      {/* Keep even the initially unpositioned popper out of document scrolling.\n          Kobalte still owns option focus and scrolls the bounded content. */}\n      <div\n        style={{\n          position: \"fixed\",\n          inset: \"0\",\n          \"pointer-events\": \"none\",\n          \"z-index\": \"50\",\n        }}\n      >\n        <SelectPrimitive.Content\n          class={cn(\n            \"bg-popover/70 text-popover-foreground ring-foreground/5 dark:ring-foreground/10 pointer-events-auto relative isolate z-50 min-w-(--kb-popper-anchor-width) overflow-hidden rounded-2xl p-1 shadow-lg ring-1 outline-none before:pointer-events-none before:absolute before:inset-0 before:-z-1 before:rounded-[inherit] before:backdrop-blur-2xl before:backdrop-saturate-150 [&_[role=listbox]]:max-h-[calc(var(--kb-popper-content-available-height)-0.5rem)] [&_[role=listbox]]:overflow-y-auto\",\n            props.class,\n            props.className\n          )}\n          ref={(element: HTMLDivElement) => {\n            setContent(element);\n\n            // Preserve the public JSX ref union: Solid compiles writable node\n            // refs into callbacks, while direct runtime callers can pass a node.\n            // oxlint-disable-next-line anti-slop/no-runtime-typeof\n            if (typeof props.ref === \"function\") props.ref(element);\n          }}\n          data-slot=\"select-content\"\n          {...rest}\n        />\n      </div>\n    </SelectPrimitive.Portal>\n  );\n};\n",
      "type": "registry:ui"
    },
    {
      "path": "lib/audiocn-license.txt",
      "content": "Audio component source adapted from https://github.com/audiocn/ui.\nMIT License — Copyright (c) 2026 OrcDev\n\nRegular SVG icon geometry adapted from @phosphor-icons/react 2.1.10.\nMIT License — Copyright (c) 2020 Phosphor Icons\n\nThe following permission and warranty terms apply to both notices.\nRetain this file with copies or substantial portions of the source.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n",
      "type": "registry:file",
      "target": "@lib/audiocn-license.txt"
    }
  ],
  "type": "registry:ui"
}
