{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "glass-slider",
  "title": "Glass Slider",
  "description": "A liquid-glass slider with a native range input back-sync: the handle lifts into a refracting lens while dragging, keeping the fill value readable through the glass, and settles back to a solid platter on release.",
  "registryDependencies": [
    "https://loreglasses.com/r/glass.json"
  ],
  "files": [
    {
      "path": "registry/default/glass-slider/glass-slider.tsx",
      "content": "\"use client\";\n\nimport { Glass, useGlassDark } from \"@/components/ui/glass\";\nimport {\n  glassEase,\n  MotionValue,\n  PRESS_DURATION,\n  prefersReducedMotion,\n  rubberBand,\n  SpringDriver,\n  TRAVEL_DURATION,\n  tween,\n} from \"@/components/ui/glass-motion\";\nimport { cn } from \"@/lib/utils\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\nconst TRACK_W = 240;\nconst TRACK_H = 6;\nconst THUMB_H = 22;\nconst THUMB_W = Math.round(2 * THUMB_H);\nconst TRAVEL = TRACK_W - THUMB_W;\nconst LENS_HALF_W = THUMB_W / 2;\nconst LENS_HALF_H = THUMB_H / 2;\nconst LENS_R = THUMB_H / 2;\nconst FAT_H = Math.round(0.75 * THUMB_H);\nconst RUBBER_OVERSHOOT = TRACK_W * 0.05;\nconst RUBBER_DAMPENING = 30;\nconst BLEED =\n  Math.ceil(0.5 * Math.max(LENS_HALF_W, LENS_HALF_H) + RUBBER_OVERSHOOT) + 2;\nconst PRESS_GROW = 1.5;\nconst TINT_BLUR = 4;\nconst REST_TINT = 1;\nconst DEFORM_STIFFNESS = 180;\nconst DEFORM_DAMPING = 14;\nconst DEFORM_CAP = 0.35;\nconst DEFORM_GAIN = 0.012;\nconst DEFORM_EXP = 0.75;\nconst HOLD_BOOST = 0.175;\n\nconst PRIMARY = \"#0a84ff\";\nconst BG4_LIGHT = \"#e1dfdf\";\nconst BG4_DARK = \"#2a2828\";\nconst SHADOW_STRONG_LIGHT = \"rgba(46, 15, 15, 0.12)\";\nconst SHADOW_STRONG_DARK = \"rgba(0, 0, 0, 0.5)\";\n\ninterface GlassSliderProps {\n  ariaLabel?: string;\n  className?: string;\n  defaultValue?: number;\n  disabled?: boolean;\n  max?: number;\n  min?: number;\n  onValueChange?: (value: number) => void;\n  step?: number;\n  value?: number;\n}\n\nfunction GlassSlider({\n  value,\n  defaultValue = 50,\n  min = 0,\n  max = 100,\n  step = 1,\n  onValueChange,\n  disabled = false,\n  ariaLabel = \"Value\",\n  className,\n}: GlassSliderProps) {\n  const dark = useGlassDark();\n  const [internal, setInternal] = useState(defaultValue);\n  const current = value ?? internal;\n  const currentRef = useRef(current);\n  currentRef.current = current;\n\n  const rootRef = useRef<HTMLDivElement | null>(null);\n  const markerRef = useRef<HTMLDivElement | null>(null);\n  const brightnessRef = useRef<HTMLDivElement | null>(null);\n  const tintRef = useRef<HTMLDivElement | null>(null);\n  const restShadowRef = useRef<HTMLDivElement | null>(null);\n  const pressShadowRef = useRef<HTMLDivElement | null>(null);\n  const fillRef = useRef<HTMLDivElement | null>(null);\n  const fatWrapRef = useRef<HTMLDivElement | null>(null);\n  const fatFillRef = useRef<HTMLDivElement | null>(null);\n  const inputRef = useRef<HTMLInputElement | null>(null);\n\n  const span = Math.max(max - min, 1e-6);\n  const toX = useCallback(\n    (v: number) => ((v - min) / span) * TRAVEL,\n    [min, span]\n  );\n  const toValue = useCallback(\n    (xv: number) => {\n      const clamped = Math.min(Math.max(xv, 0), TRAVEL);\n      const raw = min + (clamped / TRAVEL) * span;\n      return step > 0 ? Math.round((raw - min) / step) * step + min : raw;\n    },\n    [min, span, step]\n  );\n\n  const x = useRef(new MotionValue(toX(current)));\n  const lensHalfW = useRef(new MotionValue(LENS_HALF_W));\n  const lensHalfH = useRef(new MotionValue(LENS_HALF_H));\n  const lensR = useRef(new MotionValue(LENS_R));\n  const tintOpacity = useRef(new MotionValue(REST_TINT));\n  const tintBlur = useRef(new MotionValue(TINT_BLUR));\n  const fatScaleX = useRef(new MotionValue(0.85));\n  const fatScaleY = useRef(new MotionValue(0.525));\n  const shadowOpacity = useRef(new MotionValue(0));\n  const deform = useRef(new MotionValue(0));\n  const heldRef = useRef(false);\n  const activePointer = useRef<number | null>(null);\n  const grabClientX = useRef(0);\n  const grabX = useRef(0);\n  const frame = useRef(0);\n\n  const deformDriver = useRef<SpringDriver | null>(null);\n  if (!deformDriver.current) {\n    deformDriver.current = new SpringDriver(\n      deform.current,\n      { stiffness: DEFORM_STIFFNESS, damping: DEFORM_DAMPING },\n      () => {\n        const v = Math.abs(x.current.getVelocity());\n        const fromVelocity = DEFORM_GAIN * v ** DEFORM_EXP;\n        const boost = heldRef.current ? HOLD_BOOST : 0;\n        return Math.min(DEFORM_CAP, Math.max(fromVelocity, boost));\n      },\n      () => !heldRef.current && Math.abs(x.current.getVelocity()) < 0.005\n    );\n  }\n  const startDeform = useCallback(() => {\n    if (!prefersReducedMotion()) {\n      deformDriver.current?.start();\n    }\n  }, []);\n\n  const apply = useCallback(() => {\n    frame.current = 0;\n    const marker = markerRef.current;\n    if (!marker) {\n      return;\n    }\n    const xv = Math.min(\n      Math.max(x.current.get(), -RUBBER_OVERSHOOT),\n      TRAVEL + RUBBER_OVERSHOOT\n    );\n    const q = deform.current.get();\n    const w = 2 * lensHalfW.current.get() * (1 - 0.2 * q);\n    const h = 2 * lensHalfH.current.get() * (1 + 0.4 * q);\n    const r = lensR.current.get();\n    const centerX = BLEED + THUMB_W / 2 + xv;\n    const centerY = BLEED + THUMB_H / 2;\n    marker.style.width = `${w}px`;\n    marker.style.height = `${h}px`;\n    marker.style.borderRadius = `${r}px`;\n    marker.style.left = `${centerX - w / 2}px`;\n    marker.style.top = `${centerY - h / 2}px`;\n    if (tintRef.current) {\n      const t = tintOpacity.current.get();\n      tintRef.current.style.background = `rgba(255,255,255,${t})`;\n      const b = tintBlur.current.get();\n      const filter = b > 0.05 ? `blur(${b}px)` : \"none\";\n      tintRef.current.style.backdropFilter = filter;\n      tintRef.current.style.setProperty(\"-webkit-backdrop-filter\", filter);\n    }\n    const so = shadowOpacity.current.get();\n    if (restShadowRef.current) {\n      restShadowRef.current.style.opacity = String(1 - so);\n    }\n    if (pressShadowRef.current) {\n      pressShadowRef.current.style.opacity = String(so);\n    }\n    const fillW = Math.min(Math.max(xv + THUMB_W / 2, 0), TRACK_W);\n    if (fillRef.current) {\n      fillRef.current.style.width = `${fillW}px`;\n    }\n    const progress = TRAVEL > 0 ? Math.max(0, Math.min(1, xv / TRAVEL)) : 0;\n    if (fatFillRef.current) {\n      fatFillRef.current.style.transform = `translateX(${(progress - 1) * 100}%)`;\n    }\n    if (fatWrapRef.current) {\n      fatWrapRef.current.style.transform = `scale(${fatScaleX.current.get()}, ${fatScaleY.current.get()})`;\n    }\n  }, []);\n\n  const schedule = useCallback(() => {\n    if (frame.current === 0) {\n      frame.current = requestAnimationFrame(apply);\n    }\n  }, [apply]);\n\n  const syncValue = useCallback(() => {\n    const next = toValue(x.current.get());\n    if (next !== currentRef.current) {\n      if (value === undefined) {\n        setInternal(next);\n      }\n      onValueChange?.(next);\n    }\n  }, [toValue, value, onValueChange]);\n\n  useEffect(() => {\n    const values = [\n      x.current,\n      lensHalfW.current,\n      lensHalfH.current,\n      lensR.current,\n      tintOpacity.current,\n      tintBlur.current,\n      fatScaleX.current,\n      fatScaleY.current,\n      shadowOpacity.current,\n      deform.current,\n    ];\n    const subs = values.map((v) => v.on(schedule));\n    apply();\n    return () => {\n      for (const off of subs) {\n        off();\n      }\n      cancelAnimationFrame(frame.current);\n      deformDriver.current?.stop();\n    };\n  }, [apply, schedule]);\n\n  useEffect(() => {\n    if (!heldRef.current) {\n      const target = toX(current);\n      if (Math.abs(x.current.get() - target) > 0.5) {\n        const d = prefersReducedMotion() ? 0 : PRESS_DURATION;\n        tween(x.current, target, d, glassEase);\n        startDeform();\n      }\n    }\n  }, [current, toX, startDeform]);\n\n  const press = useCallback(() => {\n    const d = prefersReducedMotion() ? 0 : TRAVEL_DURATION;\n    tween(lensHalfW.current, LENS_HALF_W * PRESS_GROW, d, glassEase);\n    tween(lensHalfH.current, LENS_HALF_H * PRESS_GROW, d, glassEase);\n    tween(lensR.current, LENS_R * PRESS_GROW, d, glassEase);\n    tween(tintOpacity.current, 0, d, glassEase);\n    tween(tintBlur.current, 0, d, glassEase);\n    tween(fatScaleX.current, 0.95, d, glassEase);\n    tween(fatScaleY.current, 0.975, d, glassEase);\n    tween(shadowOpacity.current, 1, d, glassEase);\n  }, []);\n\n  const release = useCallback(() => {\n    const d = prefersReducedMotion() ? 0 : PRESS_DURATION;\n    tween(lensHalfW.current, LENS_HALF_W, d, glassEase);\n    tween(lensHalfH.current, LENS_HALF_H, d, glassEase);\n    tween(lensR.current, LENS_R, d, glassEase);\n    tween(tintOpacity.current, REST_TINT, d, glassEase);\n    tween(tintBlur.current, TINT_BLUR, d, glassEase);\n    tween(fatScaleX.current, 0.85, d, glassEase);\n    tween(fatScaleY.current, 0.525, d, glassEase);\n    tween(shadowOpacity.current, 0, d, glassEase);\n  }, []);\n\n  const onPointerDown = useCallback(\n    (e: React.PointerEvent<HTMLDivElement>) => {\n      if (disabled || activePointer.current !== null) {\n        return;\n      }\n      activePointer.current = e.pointerId;\n      e.currentTarget.setPointerCapture(e.pointerId);\n      heldRef.current = true;\n      inputRef.current?.focus({ preventScroll: true });\n      const root = rootRef.current;\n      if (!root) {\n        return;\n      }\n      const rect = root.getBoundingClientRect();\n      const localX = e.clientX - rect.left - THUMB_W / 2;\n      const clamped = Math.max(0, Math.min(TRAVEL, localX));\n      x.current.stop();\n      x.current.set(clamped);\n      syncValue();\n      grabClientX.current = e.clientX;\n      grabX.current = clamped;\n      press();\n      startDeform();\n    },\n    [disabled, press, startDeform, syncValue]\n  );\n\n  const onPointerMove = useCallback(\n    (e: React.PointerEvent<HTMLDivElement>) => {\n      if (e.pointerId !== activePointer.current) {\n        return;\n      }\n      let next = grabX.current + (e.clientX - grabClientX.current);\n      if (next < 0) {\n        next = -rubberBand(-next, RUBBER_OVERSHOOT, RUBBER_DAMPENING);\n      } else if (next > TRAVEL) {\n        next =\n          TRAVEL + rubberBand(next - TRAVEL, RUBBER_OVERSHOOT, RUBBER_DAMPENING);\n      }\n      x.current.set(next);\n      syncValue();\n    },\n    [syncValue]\n  );\n\n  const onPointerUp = useCallback(\n    (e: React.PointerEvent<HTMLDivElement>) => {\n      if (e.pointerId !== activePointer.current) {\n        return;\n      }\n      activePointer.current = null;\n      heldRef.current = false;\n      const target = Math.max(0, Math.min(TRAVEL, x.current.get()));\n      const d = prefersReducedMotion() ? 0 : PRESS_DURATION;\n      tween(x.current, target, d, glassEase);\n      startDeform();\n      release();\n    },\n    [release, startDeform]\n  );\n\n  const config = dark\n    ? {\n        scaleX: 0.133,\n        scaleY: 0.135,\n        brightness: 0.12,\n        specularAngle: 45,\n        glowExponent: 1.5,\n        edgeExponent: 1.5,\n        specularDark: false,\n      }\n    : {\n        scaleX: 0.1,\n        scaleY: 0.1,\n        brightness: -0.02,\n        specularAngle: 30,\n        glowExponent: 2,\n        edgeExponent: 1,\n        specularDark: true,\n      };\n\n  return (\n    <div\n      className={cn(\"relative inline-block touch-none\", className)}\n      style={{ flexShrink: 0, width: TRACK_W, height: THUMB_H, overflow: \"visible\" }}\n    >\n      <input\n        aria-label={ariaLabel}\n        className=\"sr-only\"\n        max={max}\n        min={min}\n        onChange={(e) => {\n          const next = Number(e.target.value);\n          if (value === undefined) {\n            setInternal(next);\n          }\n          onValueChange?.(next);\n        }}\n        ref={inputRef}\n        step={step}\n        type=\"range\"\n        value={current}\n      />\n      <div\n        aria-hidden=\"true\"\n        className=\"absolute\"\n        style={{\n          left: 0,\n          top: (THUMB_H - TRACK_H) / 2,\n          width: TRACK_W,\n          height: TRACK_H,\n          borderRadius: TRACK_H / 2,\n        }}\n      >\n        <div\n          className=\"absolute inset-0 rounded-[inherit]\"\n          style={{ background: dark ? BG4_DARK : BG4_LIGHT }}\n        />\n        <div\n          className=\"absolute rounded-[inherit]\"\n          ref={fillRef}\n          style={{\n            left: 0,\n            top: 0,\n            bottom: 0,\n            width: toX(current) + THUMB_W / 2,\n            background: PRIMARY,\n          }}\n        />\n      </div>\n      <Glass\n        chroma={0}\n        depth={2}\n        domeDepth={5}\n        edgeExponent={config.edgeExponent}\n        edgeHighlight={0.5}\n        edgeWidth={1}\n        glow={0.4}\n        glowExponent={config.glowExponent}\n        glowSpread={0.5}\n        lens={\n          <div className=\"absolute\" data-glass-lens ref={markerRef}>\n            <div\n              className=\"absolute inset-0 rounded-[inherit]\"\n              ref={brightnessRef}\n              style={{\n                background: config.brightness >= 0 ? \"#fff\" : \"#000\",\n                opacity: Math.abs(config.brightness),\n              }}\n            />\n            <div className=\"absolute inset-0 rounded-[inherit]\" ref={tintRef} />\n            <div\n              className=\"absolute inset-0 rounded-[inherit]\"\n              ref={restShadowRef}\n              style={{\n                boxShadow: `0 1.333px 5.333px ${dark ? SHADOW_STRONG_DARK : SHADOW_STRONG_LIGHT}`,\n              }}\n            />\n            <div\n              className=\"absolute inset-0 rounded-[inherit]\"\n              ref={pressShadowRef}\n              style={{\n                opacity: 0,\n                boxShadow:\n                  \"0 2px 6px rgba(0, 0, 0, 0.16), inset 0 -4px 10px rgba(0, 0, 0, 0.12)\",\n              }}\n            />\n          </div>\n        }\n        resolution={2}\n        reveal\n        scaleX={config.scaleX}\n        scaleY={config.scaleY}\n        specularAngle={config.specularAngle}\n        specularDark={config.specularDark}\n        specularStrength={1.5}\n        splay={0.5}\n        style={{\n          position: \"absolute\",\n          left: -BLEED,\n          top: -BLEED,\n          width: TRACK_W + 2 * BLEED,\n          height: THUMB_H + 2 * BLEED,\n          overflow: \"visible\",\n          pointerEvents: \"none\",\n        }}\n      >\n        <div\n          className=\"absolute overflow-hidden\"\n          ref={fatWrapRef}\n          style={{\n            left: BLEED,\n            top: BLEED + (THUMB_H - FAT_H) / 2,\n            width: TRACK_W,\n            height: FAT_H,\n            borderRadius: FAT_H / 2,\n            transform: \"scale(0.85, 0.525)\",\n          }}\n        >\n          <div\n            className=\"absolute inset-0\"\n            style={{ background: dark ? BG4_DARK : BG4_LIGHT }}\n          />\n          <div\n            className=\"absolute inset-0 rounded-[inherit]\"\n            ref={fatFillRef}\n            style={{ background: PRIMARY }}\n          />\n        </div>\n      </Glass>\n      <div\n        aria-hidden=\"true\"\n        className=\"absolute cursor-pointer\"\n        onPointerCancel={onPointerUp}\n        onPointerDown={onPointerDown}\n        onPointerMove={onPointerMove}\n        onPointerUp={onPointerUp}\n        ref={rootRef}\n        style={{ left: 0, top: 0, width: TRACK_W, height: THUMB_H }}\n      />\n    </div>\n  );\n}\n\nexport { GlassSlider };\n",
      "type": "registry:ui"
    }
  ],
  "docs": "Import { GlassSlider } from \"@/components/ui/glass-slider\". Controlled via value / onValueChange. The handle lifts into a clear lens while dragging and rubber-bands past the ends; a hidden native range input keeps it accessible. Takes no tint prop.",
  "type": "registry:ui"
}