{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "slider",
  "type": "registry:ui",
  "title": "Slider",
  "description": "Accessible horizontal and vertical numeric, range and labelled stepper sliders, including an inset XL size. No Motion dependency.",
  "dependencies": [
    "react-aria-components@^1.21.1",
    "react-aria@^3.52.1"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://components.dethink.co.uk/r/dethink-base.json"
  ],
  "files": [
    {
      "path": "packages/components/src/components/slider/slider.tsx",
      "type": "registry:file",
      "target": "~/components/dethink/components/slider/slider.tsx",
      "content": "import {\n  forwardRef,\n  useCallback,\n  useRef,\n  useState,\n  useEffect,\n  useId,\n  useContext,\n  type ReactElement,\n  type ReactNode,\n  type RefAttributes,\n} from \"react\";\nimport {\n  Label,\n  Slider as AriaSlider,\n  SliderTrack,\n  SliderOutput,\n  I18nProvider,\n  useLocale,\n  type SliderProps as AriaSliderProps,\n} from \"react-aria-components\";\nimport {\n  validateSliderSteps,\n  validateSliderValue,\n  sliderStepIndex,\n  sliderFormatOptions,\n  type SliderStep,\n} from \"./slider-values\";\nimport { SliderDecorationContext } from \"./slider-visuals\";\nimport { SliderThumbControl } from \"./slider-thumb\";\nimport { cn } from \"../../utils/cn\";\n\nexport type SliderValue = number | [number, number];\nexport type SliderSize = \"sm\" | \"md\" | \"lg\" | \"xl\";\nexport type SliderVariant = \"default\" | \"expressive\";\nexport type SliderValueDisplay = \"inline\" | \"floating\" | \"none\";\nexport interface SliderSlots {\n  track?: string;\n  fill?: string;\n  thumb?: string;\n  marks?: string;\n  output?: string;\n}\ninterface SliderBaseProps<T extends SliderValue = number> extends Omit<\n  AriaSliderProps<T>,\n  | \"children\"\n  | \"className\"\n  | \"value\"\n  | \"defaultValue\"\n  | \"onChange\"\n  | \"onChangeEnd\"\n  | \"minValue\"\n  | \"maxValue\"\n  | \"isDisabled\"\n  | \"orientation\"\n  | \"style\"\n  | \"step\"\n> {\n  value?: T;\n  defaultValue?: T;\n  onValueChange?: (value: T extends number ? number : [number, number]) => void;\n  onValueCommit?: (value: T extends number ? number : [number, number]) => void;\n  disabled?: boolean;\n  orientation?: \"horizontal\" | \"vertical\";\n  label?: ReactNode;\n  description?: ReactNode;\n  name?: string | [string, string];\n  thumbLabels?: [string, string];\n  size?: SliderSize;\n  variant?: SliderVariant;\n  valueDisplay?: SliderValueDisplay;\n  className?: string;\n  classNames?: SliderSlots;\n  locale?: string;\n}\n\nexport type SliderProps<T extends SliderValue = number> = SliderBaseProps<T> &\n  (\n    | {\n        mode?: \"numeric\";\n        min?: number;\n        max?: number;\n        step?: number;\n        steps?: never;\n      }\n    | {\n        mode: \"stepper\";\n        steps: readonly SliderStep[];\n        min?: never;\n        max?: never;\n        step?: never;\n      }\n  );\nexport type { SliderStep } from \"./slider-values\";\n\nconst thumbVisual =\n  \"border-primary bg-background group-data-[focus-visible]:ring-ring group-data-[focus-visible]:ring-offset-background pointer-events-none size-[var(--dt-slider-thumb-size)] rounded-full border-2 shadow-sm group-data-[focus-visible]:ring-2 group-data-[focus-visible]:ring-offset-2 forced-colors:border-[Highlight]\";\n\nconst sizes: Record<SliderSize, string> = {\n  sm: \"[--dt-slider-track-height:0.25rem] [--dt-slider-thumb-size:1rem]\",\n  md: \"[--dt-slider-track-height:0.375rem] [--dt-slider-thumb-size:1.25rem]\",\n  xl: \"[--dt-slider-track-height:2.75rem] [--dt-slider-thumb-size:2.125rem]\",\n  lg: \"[--dt-slider-track-height:0.5rem] [--dt-slider-thumb-size:1.5rem]\",\n};\n\nexport function sliderClassNames({\n  size = \"md\",\n  className,\n}: Pick<SliderProps, \"size\" | \"className\"> = {}) {\n  return cn(\n    \"grid w-full min-w-0 gap-density-gap text-foreground [--dt-slider-snap-duration:180ms] data-[disabled]:opacity-50\",\n    sizes[size],\n    className,\n  );\n}\n\nfunction SliderImpl(\n  {\n    value,\n    defaultValue,\n    mode = \"numeric\",\n    steps,\n    onValueChange,\n    onValueCommit,\n    min = 0,\n    max = 100,\n    step = 1,\n    disabled = false,\n    orientation = \"horizontal\",\n    label,\n    description,\n    name,\n    thumbLabels = [\"Minimum\", \"Maximum\"],\n    size = \"md\",\n    variant = \"default\",\n    valueDisplay = \"inline\",\n    className,\n    classNames,\n    locale,\n    dir,\n    \"aria-describedby\": describedBy,\n    ...props\n  }: SliderProps<SliderValue>,\n  ref: React.ForwardedRef<HTMLDivElement>,\n) {\n  const vertical = orientation === \"vertical\";\n  const Decoration = useContext(SliderDecorationContext);\n  // Store public values rather than React Aria's step indices, so a new stop\n  // list can always resolve the current selection to a valid position.\n  const [uncontrolledValue, setUncontrolledValue] = useState<SliderValue>(\n    () => defaultValue ?? (mode === \"stepper\" ? (steps?.[0]?.value ?? 0) : min),\n  );\n  const [directDrag, setDirectDrag] = useState(false);\n  useEffect(() => {\n    if (!directDrag) return;\n    const release = () => setDirectDrag(false);\n    window.addEventListener(\"pointerup\", release);\n    window.addEventListener(\"pointercancel\", release);\n    return () => {\n      window.removeEventListener(\"pointerup\", release);\n      window.removeEventListener(\"pointercancel\", release);\n    };\n  }, [directDrag]);\n  const descriptionId = useId();\n  const trackRef = useRef<HTMLDivElement>(null);\n  const inheritedLocale = useLocale();\n  const [element, setElement] = useState<HTMLDivElement | null>(null);\n  const [inheritedDirection, setInheritedDirection] = useState(\n    inheritedLocale.direction,\n  );\n  const setRoot = useCallback(\n    (node: HTMLDivElement | null) => {\n      setElement(node);\n      if (node)\n        setInheritedDirection(\n          getComputedStyle(node).direction === \"rtl\" ? \"rtl\" : \"ltr\",\n        );\n      if (typeof ref === \"function\") ref(node);\n      else if (ref) ref.current = node;\n    },\n    [ref],\n  );\n  useEffect(() => {\n    if (!element) return;\n    const observer = new MutationObserver(() =>\n      setInheritedDirection(\n        getComputedStyle(element).direction === \"rtl\" ? \"rtl\" : \"ltr\",\n      ),\n    );\n    let ancestor: HTMLElement | null = element;\n    while (ancestor) {\n      observer.observe(ancestor, {\n        attributes: true,\n        attributeFilter: [\"dir\"],\n      });\n      ancestor = ancestor.parentElement;\n    }\n    return () => observer.disconnect();\n  }, [element]);\n  const direction = dir === \"rtl\" || dir === \"ltr\" ? dir : inheritedDirection;\n  const numberFormatter = new Intl.NumberFormat(\n    locale ?? inheritedLocale.locale,\n    sliderFormatOptions(step, min, props.formatOptions),\n  );\n  // React Aria uses locale direction for input mechanics. Preserve the consumer's\n  // number locale separately while matching inherited DOM direction for movement.\n  const interactionLocale = direction === \"rtl\" ? \"ar\" : \"en-US\";\n  validateSliderValue(value);\n  validateSliderValue(defaultValue);\n  if (mode === \"stepper\") validateSliderSteps(steps!);\n  else if (![min, max, step].every(Number.isFinite) || max <= min || step <= 0)\n    throw new Error(\"Slider requires finite min < max and a positive step.\");\n  const stops = mode === \"stepper\" ? steps : undefined;\n  const toPosition = (\n    input: SliderValue | undefined,\n  ): SliderValue | undefined => {\n    if (input === undefined) return input;\n    const resolve = (number: number) =>\n      stops\n        ? sliderStepIndex(number, stops)\n        : Math.min(max, Math.max(min, number));\n    return Array.isArray(input)\n      ? [resolve(input[0]), resolve(input[1])]\n      : resolve(input);\n  };\n  const toValue = (input: SliderValue): SliderValue => {\n    if (!stops) return input;\n    return Array.isArray(input)\n      ? [stops[input[0]].value, stops[input[1]].value]\n      : stops[input].value;\n  };\n  const range = Array.isArray(value ?? uncontrolledValue);\n  return (\n    <I18nProvider locale={interactionLocale}>\n      <AriaSlider\n        {...props}\n        ref={setRoot}\n        onPointerDownCapture={(event) => {\n          props.onPointerDownCapture?.(event);\n          if (!disabled && !event.defaultPrevented)\n            setDirectDrag(\n              !!(event.target as HTMLElement).closest(\n                '[data-slot=\"slider-thumb\"]',\n              ),\n            );\n        }}\n        onPointerMoveCapture={(event) => {\n          props.onPointerMoveCapture?.(event);\n          if (!disabled && event.buttons === 1) setDirectDrag(true);\n        }}\n        dir={dir}\n        value={toPosition(value ?? uncontrolledValue)}\n        defaultValue={toPosition(defaultValue)}\n        minValue={stops ? 0 : min}\n        maxValue={stops ? stops.length - 1 : max}\n        step={stops ? 1 : step}\n        isDisabled={disabled}\n        orientation={orientation}\n        onChange={(next: SliderValue) => {\n          const nextValue = toValue(next);\n          if (value === undefined) setUncontrolledValue(nextValue);\n          onValueChange?.(nextValue);\n        }}\n        onChangeEnd={(next: SliderValue) => onValueCommit?.(toValue(next))}\n        aria-describedby={\n          [describedBy, description ? descriptionId : undefined]\n            .filter(Boolean)\n            .join(\" \") || undefined\n        }\n        data-slot=\"slider\"\n        data-size={size}\n        data-variant={variant}\n        data-mode={mode}\n        className={sliderClassNames({ size, className })}\n      >\n        {({ state }) => {\n          const formatted = state.values.map((value) =>\n            stops ? stops[value].label : numberFormatter.format(value),\n          );\n          const start = range ? state.getThumbPercent(0) * 100 : 0;\n          const end = state.getThumbPercent(state.values.length - 1) * 100;\n          return (\n            <>\n              {(label || valueDisplay !== \"none\") && (\n                <div className=\"flex min-w-0 items-baseline justify-between gap-4\">\n                  {label && (\n                    <Label className=\"text-sm font-medium\">{label}</Label>\n                  )}\n                  {valueDisplay !== \"none\" && (\n                    <SliderOutput\n                      data-slot=\"slider-output\"\n                      className={cn(\n                        \"text-muted-foreground ms-auto min-w-0 text-end text-sm font-medium tabular-nums\",\n                        classNames?.output,\n                      )}\n                    >\n                      {formatted.join(\" – \")}\n                    </SliderOutput>\n                  )}\n                </div>\n              )}\n              <div\n                className={cn(\n                  vertical ? \"flex justify-center gap-4\" : \"contents\",\n                  vertical && valueDisplay === \"floating\" && \"px-16\",\n                )}\n              >\n                <SliderTrack\n                  ref={trackRef}\n                  data-slot=\"slider-track\"\n                  className={cn(\n                    \"relative touch-none\",\n                    vertical\n                      ? \"my-[1.375rem] h-[var(--dt-slider-track-length,12rem)] w-11 shrink-0\"\n                      : \"h-11\",\n                    !vertical &&\n                      (size === \"xl\"\n                        ? \"mx-[1.375rem]\"\n                        : \"mx-[var(--dt-space-3)]\"),\n                    !vertical && valueDisplay === \"floating\" && \"mt-8\",\n                    classNames?.track,\n                  )}\n                >\n                  <div\n                    aria-hidden=\"true\"\n                    data-slot=\"slider-rail\"\n                    className={cn(\n                      \"bg-muted absolute rounded-full forced-colors:border forced-colors:border-[GrayText]\",\n                      vertical\n                        ? \"left-1/2 w-[var(--dt-slider-track-height)] -translate-x-1/2\"\n                        : \"top-1/2 h-[var(--dt-slider-track-height)] -translate-y-1/2\",\n                      size === \"xl\"\n                        ? vertical\n                          ? \"-inset-y-[1.375rem] shadow-inner\"\n                          : \"-inset-x-[1.375rem] shadow-inner\"\n                        : vertical\n                          ? \"h-full\"\n                          : \"w-full\",\n                    )}\n                  />\n                  <div\n                    aria-hidden=\"true\"\n                    data-slot=\"slider-fill\"\n                    className={cn(\n                      \"bg-primary pointer-events-none absolute rounded-full forced-colors:bg-[Highlight]\",\n                      vertical\n                        ? \"left-1/2 w-[var(--dt-slider-track-height)] -translate-x-1/2\"\n                        : \"top-1/2 h-[var(--dt-slider-track-height)] -translate-y-1/2\",\n                      variant === \"expressive\" &&\n                        \"shadow-[0_0_16px_color-mix(in_srgb,var(--dt-color-primary)_25%,transparent)]\",\n                      stops &&\n                        !directDrag &&\n                        \"motion-safe:duration-[var(--dt-slider-snap-duration)] motion-safe:ease-[cubic-bezier(0.22,1,0.36,1)]\",\n                      stops &&\n                        !directDrag &&\n                        (vertical\n                          ? \"motion-safe:transition-[height,bottom]\"\n                          : \"motion-safe:transition-[width,inset-inline-start]\"),\n                      classNames?.fill,\n                    )}\n                    style={{\n                      [vertical ? \"bottom\" : \"insetInlineStart\"]:\n                        size === \"xl\"\n                          ? `calc(${start}% - 1.375rem)`\n                          : `${start}%`,\n                      [vertical ? \"height\" : \"width\"]:\n                        size === \"xl\"\n                          ? `calc(${end - start}% + 2.75rem)`\n                          : `${end - start}%`,\n                    }}\n                  />\n                  {stops?.map((stop, index) => {\n                    const selected = state.values.includes(index);\n                    const passed =\n                      index <= state.values[state.values.length - 1] &&\n                      (!range || index >= state.values[0]);\n                    return (\n                      <span\n                        key={stop.value}\n                        aria-hidden=\"true\"\n                        data-slot=\"slider-mark\"\n                        data-selected={selected || undefined}\n                        data-passed={passed || undefined}\n                        className={cn(\n                          \"border-background bg-muted-foreground/50 data-[passed]:bg-primary data-[selected]:bg-primary pointer-events-none absolute size-2 rounded-full border-2 forced-colors:border-[Canvas] forced-colors:bg-[GrayText] forced-colors:data-[passed]:bg-[Highlight]\",\n                          vertical\n                            ? \"left-1/2 -translate-x-1/2 translate-y-1/2\"\n                            : \"top-1/2 -translate-x-1/2 -translate-y-1/2 rtl:translate-x-1/2\",\n                          classNames?.marks,\n                        )}\n                        style={{\n                          [vertical ? \"bottom\" : \"insetInlineStart\"]:\n                            `${(index / (stops.length - 1)) * 100}%`,\n                        }}\n                      />\n                    );\n                  })}\n                  {state.values.map((_, index) => (\n                    <SliderThumbControl\n                      trackRef={trackRef}\n                      orientation={orientation}\n                      valueText={formatted[index]}\n                      key={index}\n                      index={index}\n                      name={\n                        stops\n                          ? undefined\n                          : Array.isArray(name)\n                            ? name[index]\n                            : name\n                      }\n                      label={range ? thumbLabels[index] : undefined}\n                      className={cn(\n                        \"group flex size-11 cursor-grab items-center justify-center outline-none data-[disabled]:cursor-not-allowed data-[dragging]:cursor-grabbing data-[focus-visible]:z-10\",\n                        vertical ? \"left-1/2\" : \"top-1/2\",\n                        stops &&\n                          !directDrag &&\n                          \"motion-safe:duration-[var(--dt-slider-snap-duration)] motion-safe:ease-[cubic-bezier(0.22,1,0.36,1)]\",\n                        stops &&\n                          !directDrag &&\n                          (vertical\n                            ? \"motion-safe:transition-[top]\"\n                            : \"motion-safe:transition-[left]\"),\n                        classNames?.thumb,\n                      )}\n                    >\n                      {({ isDragging, isFocused, isHovered }) => (\n                        <>\n                          {valueDisplay === \"floating\" &&\n                            (isDragging ||\n                              isFocused ||\n                              (isHovered && state.focusedThumb == null)) && (\n                              <span\n                                aria-hidden=\"true\"\n                                data-slot=\"slider-floating-output\"\n                                className={cn(\n                                  \"border-border bg-background pointer-events-none absolute w-max truncate rounded-md border px-2 py-1 text-center text-xs font-medium tabular-nums shadow-sm\",\n                                  vertical\n                                    ? \"end-full me-1 max-w-16\"\n                                    : \"bottom-full mb-1 max-w-32\",\n                                )}\n                                style={\n                                  vertical\n                                    ? undefined\n                                    : {\n                                        insetInlineStart:\n                                          state.getThumbPercent(index) < 0.25\n                                            ? size === \"xl\"\n                                              ? \"0\"\n                                              : \"calc(1.375rem - var(--dt-space-3))\"\n                                            : undefined,\n                                        insetInlineEnd:\n                                          state.getThumbPercent(index) > 0.75\n                                            ? size === \"xl\"\n                                              ? \"0\"\n                                              : \"calc(1.375rem - var(--dt-space-3))\"\n                                            : undefined,\n                                      }\n                                }\n                              >\n                                {formatted[index]}\n                              </span>\n                            )}\n                          {variant === \"expressive\" && Decoration ? (\n                            <Decoration\n                              dragging={isDragging}\n                              active={isDragging || isFocused}\n                              milestone={\n                                stops ? state.values[index] : undefined\n                              }\n                              className={thumbVisual}\n                            />\n                          ) : (\n                            <span\n                              aria-hidden=\"true\"\n                              data-slot=\"slider-thumb-visual\"\n                              className={cn(\n                                thumbVisual,\n                                variant === \"expressive\" &&\n                                  \"shadow-[0_0_0_5px_color-mix(in_srgb,var(--dt-color-primary)_10%,transparent)]\",\n                              )}\n                            />\n                          )}\n                        </>\n                      )}\n                    </SliderThumbControl>\n                  ))}\n                </SliderTrack>\n                {stops && stops.some((stop) => stop.showLabel !== false) && (\n                  <div\n                    aria-hidden=\"true\"\n                    data-slot=\"slider-marks\"\n                    className={cn(\n                      \"text-muted-foreground text-[11px] leading-4\",\n                      vertical\n                        ? \"relative my-[1.375rem] h-[var(--dt-slider-track-length,12rem)] w-20\"\n                        : \"mx-[var(--dt-space-3)] grid\",\n                    )}\n                    style={\n                      vertical\n                        ? undefined\n                        : {\n                            gridTemplateColumns: `repeat(${(stops.length - 1) * 2}, minmax(0, 1fr))`,\n                          }\n                    }\n                  >\n                    {stops.map((stop, index) => (\n                      <span\n                        key={stop.value}\n                        style={\n                          vertical\n                            ? {\n                                bottom: `${(index / (stops.length - 1)) * 100}%`,\n                              }\n                            : undefined\n                        }\n                        className={cn(\n                          \"min-w-0 px-0.5 text-center [overflow-wrap:anywhere]\",\n                          vertical\n                            ? \"absolute start-0 w-full translate-y-1/2 text-start\"\n                            : index === 0\n                              ? \"-ms-[var(--dt-space-3)] w-[calc(100%+var(--dt-space-3))] text-start\"\n                              : index === stops.length - 1\n                                ? \"-me-[var(--dt-space-3)] w-[calc(100%+var(--dt-space-3))] justify-self-end text-end\"\n                                : \"col-span-2\",\n                          state.values.includes(index) &&\n                            \"text-primary font-semibold\",\n                        )}\n                      >\n                        {stop.showLabel !== false ? stop.label : null}\n                      </span>\n                    ))}\n                  </div>\n                )}\n              </div>\n              {stops &&\n                name &&\n                state.values.map((position, index) => (\n                  <input\n                    key={index}\n                    type=\"hidden\"\n                    name={Array.isArray(name) ? name[index] : name}\n                    value={stops[position].value}\n                    disabled={disabled}\n                  />\n                ))}\n\n              {description && (\n                <p\n                  id={descriptionId}\n                  className=\"text-muted-foreground text-xs leading-relaxed\"\n                >\n                  {description}\n                </p>\n              )}\n            </>\n          );\n        }}\n      </AriaSlider>\n    </I18nProvider>\n  );\n}\n\nexport const Slider = forwardRef(SliderImpl) as <\n  T extends SliderValue = number,\n>(\n  props: SliderProps<T> & RefAttributes<HTMLDivElement>,\n) => ReactElement;\n"
    },
    {
      "path": "packages/components/src/components/slider/slider-visuals.ts",
      "type": "registry:file",
      "target": "~/components/dethink/components/slider/slider-visuals.ts",
      "content": "import { createContext, type ComponentType } from \"react\";\n\n/** Internal decoration bridge: the base registry never imports Motion. */\nexport interface SliderDecorationProps {\n  dragging: boolean;\n  active: boolean;\n  milestone?: number;\n  className: string;\n}\nexport const SliderDecorationContext =\n  createContext<ComponentType<SliderDecorationProps> | null>(null);\n"
    },
    {
      "path": "packages/components/src/components/slider/slider-values.ts",
      "type": "registry:file",
      "target": "~/components/dethink/components/slider/slider-values.ts",
      "content": "export interface SliderStep {\n  value: number;\n  label: string;\n  showLabel?: boolean;\n}\n\nfunction fractionDigits(value: number) {\n  const [coefficient, exponent = \"0\"] = value.toString().split(\"e\");\n  return Math.max(\n    0,\n    (coefficient.split(\".\")[1]?.length ?? 0) - Number(exponent),\n  );\n}\n\nexport function sliderFormatOptions(\n  step: number,\n  min: number,\n  options?: Intl.NumberFormatOptions,\n): Intl.NumberFormatOptions {\n  // Explicit digit settings remain the consumer's formatting contract.\n  if (\n    options &&\n    [\n      options.minimumFractionDigits,\n      options.maximumFractionDigits,\n      options.minimumSignificantDigits,\n      options.maximumSignificantDigits,\n    ].some((value) => value !== undefined)\n  )\n    return options;\n  return {\n    maximumFractionDigits: Math.min(\n      100,\n      Math.max(3, fractionDigits(step), fractionDigits(min)),\n    ),\n    ...options,\n  };\n}\n\nexport function validateSliderSteps(steps: readonly SliderStep[]) {\n  if (\n    steps.length < 2 ||\n    steps.some(\n      (step, index) =>\n        !Number.isFinite(step.value) ||\n        !step.label.trim() ||\n        (index > 0 && step.value <= steps[index - 1].value),\n    )\n  ) {\n    throw new Error(\n      \"Slider steps require at least two finite, strictly increasing values with non-empty labels.\",\n    );\n  }\n}\n\n/** Off-stop values resolve to the nearest backing value; ties use the lower stop. */\nexport function sliderStepIndex(value: number, steps: readonly SliderStep[]) {\n  let nearest = 0;\n  for (let index = 1; index < steps.length; index++) {\n    if (\n      Math.abs(steps[index].value - value) <\n      Math.abs(steps[nearest].value - value)\n    )\n      nearest = index;\n  }\n  return nearest;\n}\n\nexport function validateSliderValue(\n  value: number | [number, number] | undefined,\n) {\n  if (value === undefined) return;\n  const values = Array.isArray(value) ? value : [value];\n  if (\n    values.some((number) => !Number.isFinite(number)) ||\n    (Array.isArray(value) && (value.length !== 2 || value[0] > value[1]))\n  ) {\n    throw new Error(\n      \"Slider values must be finite scalars or ordered two-value ranges.\",\n    );\n  }\n}\n"
    },
    {
      "path": "packages/components/src/components/slider/slider-thumb.tsx",
      "type": "registry:file",
      "target": "~/components/dethink/components/slider/slider-thumb.tsx",
      "content": "import { useContext, useRef, type ReactNode, type RefObject } from \"react\";\nimport { SliderStateContext } from \"react-aria-components\";\nimport { mergeProps, useFocusRing, useHover, useSliderThumb } from \"react-aria\";\n\nexport interface SliderThumbVisualState {\n  isDragging: boolean;\n  isFocused: boolean;\n  isHovered: boolean;\n}\n\n/** Keep React Aria's mechanics while exposing formatted value text on the native input. */\nexport function SliderThumbControl({\n  index,\n  trackRef,\n  orientation,\n  name,\n  label,\n  valueText,\n  className,\n  children,\n}: {\n  index: number;\n  trackRef: RefObject<HTMLDivElement | null>;\n  orientation: \"horizontal\" | \"vertical\";\n  name?: string;\n  label?: string;\n  valueText: string;\n  className: string;\n  children: (state: SliderThumbVisualState) => ReactNode;\n}) {\n  const state = useContext(SliderStateContext)!;\n  const inputRef = useRef<HTMLInputElement>(null);\n  const { thumbProps, inputProps, isDragging, isFocused, isDisabled } =\n    useSliderThumb(\n      { index, trackRef, inputRef, name, orientation, \"aria-label\": label },\n      state,\n    );\n  const { focusProps, isFocusVisible } = useFocusRing();\n  const { hoverProps, isHovered } = useHover({ isDisabled });\n  return (\n    <div\n      {...mergeProps(thumbProps, hoverProps)}\n      data-slot=\"slider-thumb\"\n      className={className}\n      data-dragging={isDragging || undefined}\n      data-focused={isFocused || undefined}\n      data-focus-visible={isFocusVisible || undefined}\n      data-disabled={isDisabled || undefined}\n    >\n      <input\n        {...mergeProps(inputProps, focusProps)}\n        type=\"range\"\n        ref={inputRef}\n        aria-valuetext={valueText}\n        className=\"sr-only\"\n      />\n      {children({ isDragging, isFocused, isHovered })}\n    </div>\n  );\n}\n"
    },
    {
      "path": "packages/components/src/components/slider/index.ts",
      "type": "registry:file",
      "target": "~/components/dethink/components/slider/index.ts",
      "content": "export {\n  Slider,\n  sliderClassNames,\n  type SliderProps,\n  type SliderValue,\n  type SliderSize,\n  type SliderVariant,\n  type SliderSlots,\n  type SliderValueDisplay,\n  type SliderStep,\n} from \"./slider\";\n"
    },
    {
      "path": "packages/components/src/utils/cn.ts",
      "type": "registry:file",
      "target": "~/components/dethink/utils/cn.ts",
      "content": "import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n  return twMerge(clsx(inputs));\n}\n"
    }
  ]
}
