{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "slider",
  "title": "Slider",
  "description": "Animated slider with spring-snapped thumb, step dots, range mode, and click-to-edit value display. Built on Radix UI Slider.",
  "dependencies": [
    "motion",
    "@radix-ui/react-slider"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/components/unlumen/slider/index.tsx",
      "content": "\"use client\";\n\nimport {\n  forwardRef,\n  useRef,\n  useState,\n  useEffect,\n  useCallback,\n  type HTMLAttributes,\n} from \"react\";\nimport {\n  motion,\n  useMotionValue,\n  useTransform,\n  animate,\n  AnimatePresence,\n  type MotionValue,\n} from \"motion/react\";\nimport * as SliderPrimitive from \"@radix-ui/react-slider\";\nimport { cn } from \"@/lib/utils\";\n\nconst springs = {\n  fast: { type: \"spring\" as const, duration: 0.08, bounce: 0 },\n  moderate: { type: \"spring\" as const, duration: 0.16, bounce: 0.15 },\n} as const;\n\nconst fontWeights = {\n  normal: \"'wght' 400\",\n  medium: \"'wght' 450\",\n} as const;\n\ntype SliderValue = number | [number, number];\ntype ValuePosition = \"left\" | \"right\" | \"top\" | \"bottom\" | \"tooltip\";\n\ninterface SliderProps\n  extends Omit<HTMLAttributes<HTMLDivElement>, \"onChange\" | \"defaultValue\"> {\n  value: SliderValue;\n  onChange: (value: SliderValue) => void;\n  min?: number;\n  max?: number;\n  step?: number;\n  showSteps?: boolean;\n  showValue?: boolean;\n  valuePosition?: ValuePosition;\n  formatValue?: (v: number) => string;\n  label?: string;\n  disabled?: boolean;\n}\n\nconst THUMB_SIZE = 18;\nconst THUMB_SIZE_REST = 14;\nconst TRACK_HEIGHT = 6;\nconst DOT_SIZE = 4;\n\nfunction valueToPixel(\n  v: number,\n  min: number,\n  max: number,\n  trackWidth: number,\n): number {\n  if (max === min) return 0;\n  return ((v - min) / (max - min)) * (trackWidth - THUMB_SIZE);\n}\n\nfunction pixelToValue(\n  px: number,\n  min: number,\n  max: number,\n  step: number,\n  trackWidth: number,\n): number {\n  const usable = trackWidth - THUMB_SIZE;\n  if (usable <= 0) return min;\n  const raw = (px / usable) * (max - min) + min;\n  const snapped = Math.round((raw - min) / step) * step + min;\n  return Math.max(min, Math.min(max, snapped));\n}\n\nfunction toRadixValue(value: SliderValue): number[] {\n  return Array.isArray(value) ? value : [value];\n}\n\ninterface ValueDisplayProps {\n  values: number[];\n  editingIndex: number | null;\n  onStartEdit: (index: number) => void;\n  onCommitEdit: (index: number, v: number) => void;\n  onCancelEdit: () => void;\n  min: number;\n  max: number;\n  step: number;\n  formatValue: (v: number) => string;\n  label?: string;\n  isRange: boolean;\n  isInteracting: boolean;\n}\n\nfunction ValueDisplay({\n  values,\n  editingIndex,\n  onStartEdit,\n  onCommitEdit,\n  onCancelEdit,\n  min,\n  max,\n  step,\n  formatValue,\n  label,\n  isRange,\n  isInteracting,\n}: ValueDisplayProps) {\n  const [inputValue, setInputValue] = useState(\"\");\n  const inputRef = useRef<HTMLInputElement>(null);\n\n  useEffect(() => {\n    if (editingIndex !== null) {\n      setInputValue(String(values[editingIndex]));\n      requestAnimationFrame(() => inputRef.current?.select());\n    }\n  }, [editingIndex, values]);\n\n  const commitEdit = useCallback(\n    (index: number) => {\n      const parsed = parseFloat(inputValue);\n      if (!isNaN(parsed)) {\n        const clamped = Math.max(min, Math.min(max, parsed));\n        const snapped = Math.round((clamped - min) / step) * step + min;\n        onCommitEdit(index, snapped);\n      } else {\n        onCancelEdit();\n      }\n    },\n    [inputValue, min, max, step, onCommitEdit, onCancelEdit],\n  );\n\n  const renderValue = (index: number) => {\n    if (editingIndex === index) {\n      return (\n        <span className=\"inline-grid text-[13px]\">\n          <span\n            className=\"col-start-1 row-start-1 invisible\"\n            style={{ fontVariationSettings: fontWeights.medium }}\n            aria-hidden=\"true\"\n          >\n            {label ? `${label}: ` : \"\"}\n            {formatValue(max)}\n          </span>\n          <span className=\"col-start-1 row-start-1 flex items-center gap-1\">\n            {label && <span className=\"text-muted-foreground\">{label}:</span>}\n            <input\n              ref={inputRef}\n              type=\"number\"\n              value={inputValue}\n              min={min}\n              max={max}\n              step={step}\n              onChange={(e) => setInputValue(e.target.value)}\n              onBlur={() => commitEdit(index)}\n              onKeyDown={(e) => {\n                if (e.key === \"Enter\") commitEdit(index);\n                if (e.key === \"Escape\") onCancelEdit();\n              }}\n              aria-label={`Edit slider value${isRange ? (index === 0 ? \" (start)\" : \" (end)\") : \"\"}`}\n              className=\"w-[5ch] bg-transparent text-foreground outline-none border-b border-border text-center rounded-none\"\n              style={{ fontVariationSettings: fontWeights.medium }}\n            />\n          </span>\n        </span>\n      );\n    }\n\n    return (\n      <span\n        className=\"cursor-text select-none\"\n        onClick={() => onStartEdit(index)}\n      >\n        {formatValue(values[index])}\n      </span>\n    );\n  };\n\n  return (\n    <span\n      className=\"text-[13px] text-muted-foreground transition-[font-variation-settings] duration-100 tabular-nums\"\n      style={{\n        fontVariationSettings: isInteracting\n          ? fontWeights.medium\n          : fontWeights.normal,\n      }}\n    >\n      {label && editingIndex === null && (\n        <span className=\"text-muted-foreground\">{label}: </span>\n      )}\n      {isRange ? (\n        <>\n          {renderValue(0)}\n          <span className=\"mx-1 text-muted-foreground/50\">—</span>\n          {renderValue(1)}\n        </>\n      ) : (\n        renderValue(0)\n      )}\n    </span>\n  );\n}\n\nfunction TooltipValue({\n  value,\n  formatValue,\n  motionX,\n}: {\n  value: number;\n  formatValue: (v: number) => string;\n  motionX: MotionValue<number>;\n}) {\n  const tooltipX = useTransform(motionX, (x) => x + THUMB_SIZE / 2);\n  return (\n    <motion.div\n      className=\"absolute -translate-x-1/2 pointer-events-none z-20\"\n      style={{\n        x: tooltipX,\n        top: -16,\n        transformOrigin: \"bottom center\",\n      }}\n      initial={{ opacity: 0, y: 6, scale: 0.94 }}\n      animate={{ opacity: 1, y: 0, scale: 1 }}\n      exit={{ opacity: 0, y: 6, scale: 0.94, transition: { duration: 0.1 } }}\n      transition={springs.moderate}\n    >\n      <span\n        className=\"text-[12px] text-foreground tabular-nums whitespace-nowrap bg-accent px-2 py-1 rounded-md\"\n        style={{ fontVariationSettings: fontWeights.medium }}\n      >\n        {formatValue(value)}\n      </span>\n    </motion.div>\n  );\n}\n\nfunction HoverPreviewTooltip({\n  value,\n  x,\n  formatValue,\n}: {\n  value: number;\n  x: number;\n  formatValue: (v: number) => string;\n}) {\n  const tooltipX = useMotionValue(x);\n  const hasPositioned = useRef(false);\n\n  useEffect(() => {\n    if (!hasPositioned.current) {\n      tooltipX.set(x);\n      hasPositioned.current = true;\n      return;\n    }\n\n    const controls = animate(tooltipX, x, springs.moderate);\n    return () => controls.stop();\n  }, [tooltipX, x]);\n\n  return (\n    <motion.div\n      className=\"absolute -translate-x-1/2 pointer-events-none z-20\"\n      initial={{ opacity: 0, y: 6, scale: 0.94 }}\n      animate={{ opacity: 1, y: 0, scale: 1 }}\n      exit={{ opacity: 0, y: 6, scale: 0.94, transition: { duration: 0.1 } }}\n      transition={springs.moderate}\n      style={{\n        left: tooltipX,\n        top: -20,\n        transformOrigin: \"bottom center\",\n      }}\n    >\n      <span\n        className=\"text-[12px] text-foreground tabular-nums whitespace-nowrap bg-accent px-2 py-1 rounded-md\"\n        style={{ fontVariationSettings: fontWeights.medium }}\n      >\n        {formatValue(value)}\n      </span>\n    </motion.div>\n  );\n}\n\nconst Slider = forwardRef<HTMLDivElement, SliderProps>(\n  (\n    {\n      value,\n      onChange,\n      min = 0,\n      max = 100,\n      step = 1,\n      showSteps = false,\n      showValue = true,\n      valuePosition = \"bottom\",\n      formatValue = String,\n      label,\n      disabled = false,\n      className,\n      ...props\n    },\n    ref,\n  ) => {\n    const isRange = Array.isArray(value);\n    const values = toRadixValue(value);\n\n    const trackRef = useRef<HTMLDivElement>(null);\n    const trackWidthRef = useRef(0);\n    const hasMounted = useRef(false);\n    const hasMeasuredTrack = useRef(false);\n    const dragging = useRef(false);\n    const activeDragThumb = useRef<number>(0);\n\n    const [isHovered, setIsHovered] = useState(false);\n    const [isPressed, setIsPressed] = useState(false);\n    const [editingIndex, setEditingIndex] = useState<number | null>(null);\n    const [hoverPreview, setHoverPreview] = useState<{\n      left: number;\n      width: number;\n      onFilledSide: boolean;\n      snappedValue: number;\n      cursorX: number;\n    } | null>(null);\n    const [hoverThumbIndex, setHoverThumbIndex] = useState<number | null>(null);\n\n    const motionX0 = useMotionValue(0);\n    const motionX1 = useMotionValue(0);\n\n    const fillLeft = useTransform(motionX0, (x) =>\n      isRange ? x + THUMB_SIZE / 2 : 0,\n    );\n    const fillWidthSingle = useTransform(motionX0, (x) => x + THUMB_SIZE / 2);\n    const fillWidthRange = useTransform(\n      [motionX0, motionX1] as MotionValue<number>[],\n      ([x0, x1]) => (x1 as number) - (x0 as number),\n    );\n    const fillWidth = isRange ? fillWidthRange : fillWidthSingle;\n\n    const computeHoverPreview = useCallback(\n      (cursorX: number, trackWidth: number) => {\n        const rawVal = (cursorX / trackWidth) * (max - min) + min;\n        const snappedVal = Math.max(\n          min,\n          Math.min(max, Math.round((rawVal - min) / step) * step + min),\n        );\n        const snappedX = ((snappedVal - min) / (max - min)) * trackWidth;\n\n        const c0 = motionX0.get() + THUMB_SIZE / 2;\n        const c1 = motionX1.get() + THUMB_SIZE / 2;\n        const nearestIdx = isRange\n          ? Math.abs(snappedX - c0) <= Math.abs(snappedX - c1)\n            ? 0\n            : 1\n          : 0;\n        const nearest = nearestIdx === 0 ? c0 : c1;\n        const onFilledSide = isRange\n          ? snappedX > c0 && snappedX < c1\n          : snappedX < c0;\n\n        setHoverPreview({\n          left: Math.min(nearest, snappedX),\n          width: Math.abs(snappedX - nearest),\n          onFilledSide,\n          snappedValue: snappedVal,\n          cursorX: snappedX,\n        });\n        setHoverThumbIndex(nearestIdx);\n      },\n      [min, max, step, isRange, motionX0, motionX1],\n    );\n\n    useEffect(() => {\n      hasMounted.current = true;\n    }, []);\n\n    useEffect(() => {\n      const el = trackRef.current;\n      if (!el) return;\n      const ro = new ResizeObserver(([entry]) => {\n        trackWidthRef.current = entry.contentRect.width;\n        if (dragging.current) return;\n        const px0 = valueToPixel(values[0], min, max, entry.contentRect.width);\n        const shouldAnimate = hasMounted.current && hasMeasuredTrack.current;\n        shouldAnimate\n          ? animate(motionX0, px0, springs.moderate)\n          : motionX0.set(px0);\n        if (isRange && values[1] !== undefined) {\n          const px1 = valueToPixel(\n            values[1],\n            min,\n            max,\n            entry.contentRect.width,\n          );\n          shouldAnimate\n            ? animate(motionX1, px1, springs.moderate)\n            : motionX1.set(px1);\n        }\n        hasMeasuredTrack.current = true;\n      });\n      ro.observe(el);\n      return () => ro.disconnect();\n    }, [min, max, isRange, values, motionX0, motionX1]);\n\n    useEffect(() => {\n      if (dragging.current) return;\n      const tw = trackWidthRef.current;\n      if (tw <= 0) return;\n      const px0 = valueToPixel(values[0], min, max, tw);\n      hasMounted.current\n        ? animate(motionX0, px0, springs.moderate)\n        : motionX0.set(px0);\n      if (isRange && values[1] !== undefined) {\n        const px1 = valueToPixel(values[1], min, max, tw);\n        hasMounted.current\n          ? animate(motionX1, px1, springs.moderate)\n          : motionX1.set(px1);\n      }\n    }, [values, min, max, isRange, motionX0, motionX1]);\n\n    const clampForRange = useCallback(\n      (px: number, thumbIndex: number): number => {\n        if (!isRange) return px;\n        return thumbIndex === 0\n          ? Math.min(px, motionX1.get() - THUMB_SIZE * 0.5)\n          : Math.max(px, motionX0.get() + THUMB_SIZE * 0.5);\n      },\n      [isRange, motionX0, motionX1],\n    );\n\n    const emitChange = useCallback(\n      (thumbIndex: number, newValue: number) => {\n        if (isRange) {\n          const newValues: [number, number] = [...(values as [number, number])];\n          newValues[thumbIndex] = newValue;\n          onChange(newValues);\n        } else {\n          onChange(newValue);\n        }\n      },\n      [isRange, values, onChange],\n    );\n\n    const handlePointerDown = useCallback(\n      (e: React.PointerEvent<HTMLDivElement>) => {\n        if (disabled) return;\n        if (e.pointerType === \"mouse\" && e.button !== 0) return;\n        e.preventDefault();\n        e.stopPropagation();\n\n        const trackRect = trackRef.current?.getBoundingClientRect();\n        if (!trackRect) return;\n\n        const localX = e.clientX - trackRect.left - THUMB_SIZE / 2;\n        const clamped = Math.max(\n          0,\n          Math.min(trackRect.width - THUMB_SIZE, localX),\n        );\n\n        if (isRange) {\n          const dist0 = Math.abs(clamped - motionX0.get());\n          const dist1 = Math.abs(clamped - motionX1.get());\n          activeDragThumb.current = dist0 <= dist1 ? 0 : 1;\n        } else {\n          activeDragThumb.current = 0;\n        }\n\n        dragging.current = true;\n        setIsPressed(true);\n\n        const motionX = activeDragThumb.current === 0 ? motionX0 : motionX1;\n        const snappedValue = pixelToValue(\n          clamped,\n          min,\n          max,\n          step,\n          trackRect.width,\n        );\n        const snappedPx = valueToPixel(snappedValue, min, max, trackRect.width);\n        const finalPx = clampForRange(snappedPx, activeDragThumb.current);\n\n        animate(motionX, finalPx, springs.moderate);\n        emitChange(\n          activeDragThumb.current,\n          pixelToValue(finalPx, min, max, step, trackRect.width),\n        );\n\n        setHoverPreview((prev) => ({\n          left: prev?.left ?? 0,\n          width: prev?.width ?? 0,\n          onFilledSide: prev?.onFilledSide ?? false,\n          snappedValue,\n          cursorX: finalPx + THUMB_SIZE / 2,\n        }));\n\n        (e.currentTarget as HTMLElement).setPointerCapture(e.pointerId);\n      },\n      [\n        disabled,\n        isRange,\n        min,\n        max,\n        step,\n        motionX0,\n        motionX1,\n        clampForRange,\n        emitChange,\n      ],\n    );\n\n    const handlePointerMove = useCallback(\n      (e: React.PointerEvent<HTMLDivElement>) => {\n        if (!dragging.current) return;\n        e.stopPropagation();\n        const trackRect = trackRef.current?.getBoundingClientRect();\n        if (!trackRect) return;\n\n        const localX = e.clientX - trackRect.left - THUMB_SIZE / 2;\n        const clamped = Math.max(\n          0,\n          Math.min(trackRect.width - THUMB_SIZE, localX),\n        );\n        const motionX = activeDragThumb.current === 0 ? motionX0 : motionX1;\n        const snappedValue = pixelToValue(\n          clamped,\n          min,\n          max,\n          step,\n          trackRect.width,\n        );\n        const snappedPx = valueToPixel(snappedValue, min, max, trackRect.width);\n        const finalPx = clampForRange(snappedPx, activeDragThumb.current);\n\n        motionX.set(finalPx);\n        emitChange(\n          activeDragThumb.current,\n          pixelToValue(finalPx, min, max, step, trackRect.width),\n        );\n\n        setHoverPreview((prev) => ({\n          left: prev?.left ?? 0,\n          width: prev?.width ?? 0,\n          onFilledSide: prev?.onFilledSide ?? false,\n          snappedValue,\n          cursorX: finalPx + THUMB_SIZE / 2,\n        }));\n      },\n      [min, max, step, motionX0, motionX1, clampForRange, emitChange],\n    );\n\n    const handlePointerUp = useCallback(() => {\n      if (!dragging.current) return;\n      dragging.current = false;\n      setIsPressed(false);\n      const tw = trackWidthRef.current;\n      const motionX = activeDragThumb.current === 0 ? motionX0 : motionX1;\n      const snapped = pixelToValue(motionX.get(), min, max, step, tw);\n      animate(motionX, valueToPixel(snapped, min, max, tw), springs.moderate);\n    }, [min, max, step, motionX0, motionX1]);\n\n    const handleRadixChange = useCallback(\n      (newValues: number[]) => {\n        if (dragging.current) return;\n        onChange(isRange ? (newValues as [number, number]) : newValues[0]);\n      },\n      [isRange, onChange],\n    );\n\n    const stepDots = showSteps\n      ? Array.from({ length: Math.round((max - min) / step) + 1 }, (_, i) => {\n          const v = min + i * step;\n          return { value: v, percent: (v - min) / (max - min) };\n        })\n      : [];\n\n    const isInteracting = isHovered || isPressed;\n\n    const valueDisplay = showValue && valuePosition !== \"tooltip\" && (\n      <ValueDisplay\n        values={values}\n        editingIndex={editingIndex}\n        onStartEdit={(i) => setEditingIndex(i)}\n        onCommitEdit={(i, v) => {\n          emitChange(i, v);\n          setEditingIndex(null);\n        }}\n        onCancelEdit={() => setEditingIndex(null)}\n        min={min}\n        max={max}\n        step={step}\n        formatValue={formatValue}\n        label={label}\n        isRange={isRange}\n        isInteracting={isInteracting}\n      />\n    );\n\n    const renderVisualThumb = (index: number) => {\n      const motionX = index === 0 ? motionX0 : motionX1;\n      return (\n        <motion.span\n          key={`visual-thumb-${index}`}\n          className=\"flex items-center justify-center pointer-events-none absolute top-1/2\"\n          style={{\n            width: THUMB_SIZE,\n            height: THUMB_SIZE,\n            marginTop: -THUMB_SIZE / 2,\n            x: motionX,\n            left: 0,\n            zIndex: 10,\n          }}\n          initial={false}\n          transition={springs.moderate}\n        >\n          <motion.span\n            className=\"block rounded-full\"\n            initial={false}\n            animate={{\n              width:\n                hoverThumbIndex === index ||\n                (isPressed && activeDragThumb.current === index)\n                  ? THUMB_SIZE\n                  : THUMB_SIZE_REST,\n              height:\n                hoverThumbIndex === index ||\n                (isPressed && activeDragThumb.current === index)\n                  ? THUMB_SIZE\n                  : THUMB_SIZE_REST,\n            }}\n            transition={springs.fast}\n            style={{\n              backgroundColor: \"white\",\n              boxShadow:\n                \"0 1px 4px rgba(0,0,0,0.15), 0 0 0 1px rgba(0,0,0,0.06)\",\n            }}\n          />\n        </motion.span>\n      );\n    };\n\n    return (\n      <div\n        ref={ref}\n        className={cn(\n          \"flex w-full select-none touch-none overflow-visible\",\n          valuePosition === \"left\" || valuePosition === \"right\"\n            ? \"flex-row items-center gap-3\"\n            : \"flex-col gap-2\",\n          disabled && \"opacity-50 pointer-events-none\",\n          className,\n        )}\n        {...props}\n      >\n        {(valuePosition === \"top\" || valuePosition === \"left\") && valueDisplay}\n\n        <div\n          className=\"relative flex-1 overflow-visible\"\n          style={{\n            height: THUMB_SIZE + (valuePosition === \"tooltip\" ? 16 : 0),\n            paddingTop: valuePosition === \"tooltip\" ? 16 : 0,\n          }}\n          onPointerEnter={() => setIsHovered(true)}\n          onPointerLeave={() => {\n            setIsHovered(false);\n            setHoverPreview(null);\n            setHoverThumbIndex(null);\n          }}\n          onMouseMove={(e) => {\n            if (dragging.current) return;\n            const trackRect = trackRef.current?.getBoundingClientRect();\n            if (!trackRect) return;\n            const x = e.clientX - trackRect.left;\n            computeHoverPreview(\n              Math.max(0, Math.min(trackRect.width, x)),\n              trackRect.width,\n            );\n          }}\n        >\n          {showValue && valuePosition === \"tooltip\" && (\n            <AnimatePresence>\n              {isInteracting && (\n                <TooltipValue\n                  key=\"tip-0\"\n                  value={values[0]}\n                  formatValue={formatValue}\n                  motionX={motionX0}\n                />\n              )}\n              {isInteracting && isRange && values[1] !== undefined && (\n                <TooltipValue\n                  key=\"tip-1\"\n                  value={values[1]}\n                  formatValue={formatValue}\n                  motionX={motionX1}\n                />\n              )}\n            </AnimatePresence>\n          )}\n\n          {/* invisible radix — keyboard/ARIA only */}\n          <SliderPrimitive.Root\n            value={values}\n            onValueChange={handleRadixChange}\n            min={min}\n            max={max}\n            step={step}\n            disabled={disabled}\n            aria-label={label}\n            className=\"absolute inset-0 opacity-0 pointer-events-none\"\n            style={{ height: THUMB_SIZE }}\n          >\n            <SliderPrimitive.Track className=\"w-full h-full\">\n              <SliderPrimitive.Range />\n            </SliderPrimitive.Track>\n            <SliderPrimitive.Thumb\n              className=\"block outline-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background\"\n              style={{ width: THUMB_SIZE, height: THUMB_SIZE }}\n            />\n            {isRange && (\n              <SliderPrimitive.Thumb\n                className=\"block outline-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background\"\n                style={{ width: THUMB_SIZE, height: THUMB_SIZE }}\n              />\n            )}\n          </SliderPrimitive.Root>\n\n          <div\n            ref={trackRef}\n            className=\"relative w-full cursor-pointer\"\n            style={{ height: THUMB_SIZE + 16 }}\n            onPointerDown={handlePointerDown}\n            onPointerMove={handlePointerMove}\n            onPointerUp={handlePointerUp}\n          >\n            <div\n              className=\"absolute cursor-pointer\"\n              style={{ left: -8, right: -8, top: 0, bottom: 0 }}\n              onPointerDown={handlePointerDown}\n              onPointerMove={handlePointerMove}\n              onPointerUp={handlePointerUp}\n            />\n\n            <AnimatePresence>\n              {hoverPreview && valuePosition !== \"tooltip\" && (\n                <HoverPreviewTooltip\n                  key=\"hover-tip\"\n                  value={hoverPreview.snappedValue}\n                  x={hoverPreview.cursorX}\n                  formatValue={formatValue}\n                />\n              )}\n            </AnimatePresence>\n\n            <motion.div\n              className=\"absolute left-0 right-0 rounded-full\"\n              initial={false}\n              animate={{\n                height: isHovered || isPressed ? 8 : TRACK_HEIGHT,\n                top:\n                  isHovered || isPressed\n                    ? 8 + (THUMB_SIZE - 8) / 2\n                    : 8 + (THUMB_SIZE - TRACK_HEIGHT) / 2,\n              }}\n              transition={springs.fast}\n              style={{ backgroundColor: \"var(--accent)\" }}\n            >\n              <motion.div\n                className=\"absolute h-full rounded-full\"\n                style={{\n                  left: fillLeft,\n                  width: fillWidth,\n                  backgroundColor: \"var(--foreground)\",\n                }}\n              />\n\n              <motion.div\n                className=\"absolute h-full pointer-events-none rounded-full\"\n                initial={false}\n                animate={{\n                  left:\n                    hoverPreview && !hoverPreview.onFilledSide\n                      ? hoverPreview.left\n                      : 0,\n                  width:\n                    hoverPreview && !hoverPreview.onFilledSide\n                      ? hoverPreview.width\n                      : 0,\n                  opacity:\n                    hoverPreview && !hoverPreview.onFilledSide && !isPressed\n                      ? 1\n                      : 0,\n                }}\n                transition={{\n                  ...springs.moderate,\n                  opacity: { duration: 0.15 },\n                }}\n                style={{\n                  backgroundColor:\n                    \"color-mix(in srgb, var(--foreground) 20%, transparent)\",\n                }}\n              />\n\n              <motion.div\n                className=\"absolute h-full pointer-events-none z-[2] rounded-full\"\n                initial={false}\n                animate={{\n                  left: hoverPreview?.onFilledSide ? hoverPreview.left : 0,\n                  width: hoverPreview?.onFilledSide ? hoverPreview.width : 0,\n                  opacity: hoverPreview?.onFilledSide && !isPressed ? 1 : 0,\n                }}\n                transition={{\n                  ...springs.moderate,\n                  opacity: { duration: 0.15 },\n                }}\n                style={{\n                  backgroundColor:\n                    \"color-mix(in srgb, var(--background) 25%, transparent)\",\n                }}\n              />\n            </motion.div>\n\n            {stepDots.map(({ value: v, percent }) => {\n              const onFilled = isRange\n                ? v >= values[0] && v <= values[1]\n                : v <= values[0];\n              return (\n                <div\n                  key={v}\n                  className=\"absolute pointer-events-none flex items-center justify-center\"\n                  style={{\n                    left: `calc(${THUMB_SIZE / 2}px + ${percent} * (100% - ${THUMB_SIZE}px))`,\n                    top: \"50%\",\n                    width: 0,\n                    height: 0,\n                  }}\n                >\n                  <motion.div\n                    className=\"relative rounded-full flex-shrink-0 z-[6]\"\n                    initial={false}\n                    animate={{\n                      width: isHovered ? DOT_SIZE * 1.25 : DOT_SIZE,\n                      height: isHovered ? DOT_SIZE * 1.25 : DOT_SIZE,\n                    }}\n                    transition={springs.moderate}\n                    style={{\n                      backgroundColor: onFilled\n                        ? \"color-mix(in srgb, var(--background) 20%, var(--foreground))\"\n                        : \"color-mix(in srgb, var(--muted-foreground) 40%, var(--accent))\",\n                    }}\n                  />\n                </div>\n              );\n            })}\n\n            {renderVisualThumb(0)}\n            {isRange && renderVisualThumb(1)}\n          </div>\n        </div>\n\n        {(valuePosition === \"bottom\" || valuePosition === \"right\") &&\n          valueDisplay}\n      </div>\n    );\n  },\n);\n\nSlider.displayName = \"Slider\";\n\nexport { Slider };\nexport type { SliderProps, SliderValue, ValuePosition };\n",
      "type": "registry:ui",
      "target": "components/unlumen-ui/slider.tsx"
    }
  ],
  "type": "registry:ui"
}