{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "glass",
  "title": "Glass",
  "description": "A cross-browser liquid-glass lens engine built on SVG feDisplacementMap. Refracts live DOM content through movable lens regions with a circle-profile bezel, dome bulge, chromatic aberration, and baked specular highlights. Ships with a dependency-free motion core (overshoot tweens, springs, rubber-band) for gel-like interactions. Independent implementation of the displacement-map technique described in Aave Labs' 'Building Glass for the Web'.",
  "files": [
    {
      "path": "registry/default/glass/glass.tsx",
      "content": "\"use client\";\n\nimport {\n  GlassWebGLRenderer,\n  isRasterChild,\n  webglAvailable,\n} from \"@/components/ui/glass-webgl\";\nimport { cn } from \"@/lib/utils\";\nimport {\n  type ComponentProps,\n  type ReactNode,\n  type RefObject,\n  useCallback,\n  useEffect,\n  useId,\n  useRef,\n  useState,\n  useSyncExternalStore,\n} from \"react\";\n\nconst SLOTS = 5;\nconst MAP_CACHE_LIMIT = 64;\nconst EDGE_BIAS = 0.5;\n\ninterface MapParams {\n  depth: number;\n  domeDepth: number;\n  edgeExponent: number;\n  edgeStrength: number;\n  edgeWidth: number;\n  glowExponent: number;\n  glowSpread: number;\n  glowStrength: number;\n  halfH: number;\n  halfW: number;\n  radius: number;\n  size: number;\n  splay: number;\n  specularAngle: number;\n  sdfBoundary?: boolean;\n  edgeFalloff?: boolean;\n  alphaBlend?: boolean;\n}\n\ninterface GlassDynamics {\n  zoom?: number;\n  depthMul?: number;\n  mapDims?: { halfW: number; halfH: number; radius: number };\n}\n\nfunction clamp01(v: number): number {\n  return v < 0 ? 0 : v > 1 ? 1 : v;\n}\n\nfunction erf(x: number): number {\n  return Math.tanh(1.7724538509 * x);\n}\n\nfunction sphereAvgSlope(r: number, halfDim: number): number {\n  let sum = 0;\n  for (let i = 0; i <= 200; i++) {\n    const a = (i / 200) * halfDim;\n    const s = a / Math.sqrt(r * r - a * a);\n    sum += i === 0 || i === 200 ? 0.5 * s : s;\n  }\n  return sum / 200;\n}\n\ninterface DomeConstants {\n  rx: number;\n  ry: number;\n  scaleX: number;\n  scaleY: number;\n}\n\nfunction computeDomeConstants(\n  domeDepth: number,\n  halfW: number,\n  halfH: number\n): DomeConstants {\n  const h = Math.max(0.01, Math.min(domeDepth, Math.min(halfW, halfH) - 1));\n  const rx = (halfW * halfW + h * h) / (2 * h);\n  const ry = (halfH * halfH + h * h) / (2 * h);\n  const ax = sphereAvgSlope(rx, halfW);\n  const ay = sphereAvgSlope(ry, halfH);\n  return {\n    rx,\n    ry,\n    scaleX: ax > 0 ? 0.5 / ax : 1,\n    scaleY: ay > 0 ? 0.5 / ay : 1,\n  };\n}\n\nfunction domeGradient(p: number, r: number, scale: number): number {\n  const c = Math.min(p, 0.999 * r);\n  return (c / Math.sqrt(r * r - c * c)) * scale;\n}\n\nfunction generateLensMap(p: MapParams): string | null {\n  if (p.halfW < 2 || p.halfH < 2) {\n    return null;\n  }\n  const sdfBoundary = p.sdfBoundary ?? true;\n  const edgeFalloff = p.edgeFalloff ?? true;\n  const size = p.size;\n  const canvas = document.createElement(\"canvas\");\n  canvas.width = size;\n  canvas.height = size;\n  const ctx = canvas.getContext(\"2d\");\n  if (!ctx) {\n    return null;\n  }\n  const image = ctx.createImageData(size, size);\n  const data = image.data;\n  const half = size >> 1;\n  const r0 = Math.min(p.radius, p.halfW, p.halfH);\n  const innerHalfW = Math.max(0, p.halfW - p.depth);\n  const innerHalfH = Math.max(0, p.halfH - p.depth);\n  const innerR = Math.max(0, Math.min(p.radius, Math.min(innerHalfW, innerHalfH)));\n  const falloffInv = p.depth > 0 ? 1 / (p.depth * Math.SQRT2) : 1e6;\n  const specOn = p.glowStrength > 0 || p.edgeStrength > 0;\n  const theta = (p.specularAngle * Math.PI) / 180;\n  const cosT = Math.cos(theta);\n  const sinT = Math.sin(theta);\n  const spreadStart = (1 - p.glowSpread) * Math.SQRT2;\n  const spreadRange = p.glowSpread * Math.SQRT2;\n  const spreadInv = spreadRange > 0.001 ? 1 / spreadRange : 0;\n  const edgeWInv = p.edgeWidth > 0 ? 1 / p.edgeWidth : 0;\n  const alphaBlend = p.alphaBlend ?? false;\n  const alphaInv = alphaBlend && p.depth > 0 ? 1 / p.depth : 0;\n  const stepX = (2 * p.halfW) / size;\n  const stepY = (2 * p.halfH) / size;\n  const invW = 1 / p.halfW;\n  const invH = 1 / p.halfH;\n  const dome = p.domeDepth > 0;\n  const splaying = p.splay < 1;\n  const proxHalf = 0.5 * Math.min(p.halfW, p.halfH);\n  const proxInv = proxHalf > 0 ? 1 / proxHalf : 0;\n  let colSlope: Float32Array | null = null;\n  let domeRy = 0;\n  let domeScaleY = 0;\n  if (dome) {\n    const dc = computeDomeConstants(p.domeDepth, p.halfW, p.halfH);\n    domeRy = dc.ry;\n    domeScaleY = dc.scaleY;\n    colSlope = new Float32Array(half);\n    const lim = 0.999 * dc.rx;\n    for (let col = 0; col < half; col++) {\n      const px = p.halfW - (col + 0.5) * stepX;\n      const s = px < lim ? px : lim;\n      colSlope[col] = (s / Math.sqrt(dc.rx * dc.rx - s * s)) * dc.scaleX;\n    }\n  }\n  for (let row = 0; row < half; row++) {\n    const py = p.halfH - (row + 0.5) * stepY;\n    const cornerDy = py - p.halfH + r0;\n    const innerDy = py - innerHalfH + innerR;\n    const yLin = Math.min(py * invH, 1);\n    const domeY = dome ? domeGradient(py, domeRy, domeScaleY) : yLin;\n    const proxY = splaying ? Math.max(0, 1 - (p.halfH - py) * proxInv) : 0;\n    const mr = size - 1 - row;\n    for (let col = 0; col < half; col++) {\n      const px = p.halfW - (col + 0.5) * stepX;\n      const mc = size - 1 - col;\n      const cornerDx = px - p.halfW + r0;\n      const ux = Math.max(cornerDx, 0);\n      const uy = Math.max(cornerDy, 0);\n      const sd =\n        Math.hypot(ux, uy) + Math.min(Math.max(cornerDx, cornerDy), 0) - r0;\n      const i00 = (row * size + col) * 4;\n      const i10 = (row * size + mc) * 4;\n      const i01 = (mr * size + col) * 4;\n      const i11 = (mr * size + mc) * 4;\n      if (sdfBoundary && sd >= 0) {\n        const edgeAlpha = alphaBlend ? 0 : 255;\n        for (const i of [i00, i10, i01, i11]) {\n          data[i] = 128;\n          data[i + 1] = 128;\n          data[i + 2] = 128;\n          data[i + 3] = edgeAlpha;\n        }\n        continue;\n      }\n      let dx = colSlope ? colSlope[col] : Math.min(px * invW, 1);\n      let dy = domeY;\n      if (splaying) {\n        const k = 1 - p.splay;\n        const ty = proxY * k;\n        const tx = Math.max(0, 1 - (p.halfW - px) * proxInv) * k;\n        if (tx > 0.001 || ty > 0.001) {\n          const ox = dx;\n          const oy = dy;\n          dx = ox * (1 - ty);\n          dy = oy * (1 - tx);\n          const before = Math.hypot(ox, oy);\n          const after = Math.hypot(dx, dy);\n          if (after > 0.001) {\n            const f = before / after;\n            dx *= f;\n            dy *= f;\n          }\n        }\n      }\n      let gate = 1;\n      if (edgeFalloff) {\n        const innerDx = px - innerHalfW + innerR;\n        const iux = Math.max(innerDx, 0);\n        const iuy = Math.max(innerDy, 0);\n        const isd =\n          Math.hypot(iux, iuy) +\n          Math.min(Math.max(innerDx, innerDy), 0) -\n          innerR;\n        gate = 0.5 * (1 + erf(isd * falloffInv));\n      }\n      const hx = 0.5 * dx * gate;\n      const hy = 0.5 * dy * gate;\n      const rPos = ((0.5 + hx) * 255 + 0.5) | 0;\n      const rNeg = ((0.5 - hx) * 255 + 0.5) | 0;\n      const gPos = ((0.5 + hy) * 255 + 0.5) | 0;\n      const gNeg = ((0.5 - hy) * 255 + 0.5) | 0;\n      let b1 = 128;\n      let b2 = 128;\n      if (specOn) {\n        const sa = Math.min(px * invW, 1) * cosT;\n        const sb = yLin * sinT;\n        const f1 = Math.abs(sa + sb);\n        const f2 = Math.abs(sa - sb);\n        let s1 = 0;\n        let s2 = 0;\n        if (p.glowStrength > 0) {\n          s1 +=\n            p.glowStrength *\n            clamp01((f1 - spreadStart) * spreadInv) ** p.glowExponent *\n            gate;\n          s2 +=\n            p.glowStrength *\n            clamp01((f2 - spreadStart) * spreadInv) ** p.glowExponent *\n            gate;\n        }\n        if (p.edgeStrength > 0) {\n          const rim = sd < 0 ? Math.max(0, 1 + sd * edgeWInv) : 0;\n          s1 += p.edgeStrength * rim * f1 ** p.edgeExponent;\n          s2 += p.edgeStrength * rim * f2 ** p.edgeExponent;\n        }\n        if (s1 > 1) {\n          s1 = 1;\n        }\n        if (s2 > 1) {\n          s2 = 1;\n        }\n        b1 = (127 * s1 + 128 + 0.5) | 0;\n        b2 = (127 * s2 + 128 + 0.5) | 0;\n      }\n      const aIn = alphaBlend ? ((clamp01(-sd * alphaInv) * 255 + 0.5) | 0) : 255;\n      data[i00] = rPos;\n      data[i00 + 1] = gPos;\n      data[i00 + 2] = b1;\n      data[i00 + 3] = aIn;\n      data[i10] = rNeg;\n      data[i10 + 1] = gPos;\n      data[i10 + 2] = b2;\n      data[i10 + 3] = aIn;\n      data[i01] = rPos;\n      data[i01 + 1] = gNeg;\n      data[i01 + 2] = b2;\n      data[i01 + 3] = aIn;\n      data[i11] = rNeg;\n      data[i11 + 1] = gNeg;\n      data[i11 + 2] = b1;\n      data[i11 + 3] = aIn;\n    }\n  }\n  ctx.putImageData(image, 0, 0);\n  return canvas.toDataURL(\"image/png\");\n}\n\nconst maskCache = new Map<string, string>();\n\nfunction roundedMaskHref(\n  w: number,\n  h: number,\n  rx: number,\n  ry: number\n): string {\n  const cw = Math.max(1, Math.round(w));\n  const ch = Math.max(1, Math.round(h));\n  const crx = Math.max(0, Math.min(rx, cw / 2));\n  const cry = Math.max(0, Math.min(ry, ch / 2));\n  const key = `${cw}|${ch}|${crx}|${cry}`;\n  const cached = maskCache.get(key);\n  if (cached !== undefined) {\n    return cached;\n  }\n  const canvas = document.createElement(\"canvas\");\n  canvas.width = cw;\n  canvas.height = ch;\n  const ctx = canvas.getContext(\"2d\");\n  if (!ctx) {\n    return \"\";\n  }\n  ctx.fillStyle = \"#fff\";\n  ctx.beginPath();\n  ctx.roundRect(0, 0, cw, ch, [{ x: crx, y: cry }]);\n  ctx.fill();\n  const url = canvas.toDataURL();\n  maskCache.set(key, url);\n  if (maskCache.size > MAP_CACHE_LIMIT) {\n    const first = maskCache.keys().next().value;\n    if (first !== undefined) {\n      maskCache.delete(first);\n    }\n  }\n  return url;\n}\n\nlet emptyHref: string | null = null;\n\nfunction placeholderHref(): string {\n  if (emptyHref === null && typeof document !== \"undefined\") {\n    const canvas = document.createElement(\"canvas\");\n    canvas.width = 1;\n    canvas.height = 1;\n    emptyHref = canvas.toDataURL();\n  }\n  return emptyHref ?? \"\";\n}\n\nconst isWebKitOnly =\n  typeof navigator !== \"undefined\" &&\n  /AppleWebKit/.test(navigator.userAgent) &&\n  !/(Chrome|Chromium|CriOS|FxiOS|EdgiOS|Edg|OPR|Firefox)/.test(\n    navigator.userAgent\n  );\n\nfunction isSafariBrowser(): boolean {\n  return isWebKitOnly;\n}\n\nfunction readForcedRenderer(): \"webgl\" | \"svg\" | null {\n  if (typeof window === \"undefined\") {\n    return null;\n  }\n  try {\n    const query = new URLSearchParams(window.location.search).get(\n      \"glassRenderer\"\n    );\n    if (query === \"webgl\" || query === \"svg\") {\n      return query;\n    }\n    const flag = (window as unknown as { __GLASS_RENDERER__?: string })\n      .__GLASS_RENDERER__;\n    if (flag === \"webgl\" || flag === \"svg\") {\n      return flag;\n    }\n    const stored = window.localStorage?.getItem(\"glassRenderer\");\n    if (stored === \"webgl\" || stored === \"svg\") {\n      return stored;\n    }\n  } catch {\n    return null;\n  }\n  return null;\n}\n\nconst forcedRenderer = readForcedRenderer();\n\nfunction useGlassDark(): boolean {\n  const [dark, setDark] = useState(\n    () =>\n      typeof document !== \"undefined\" &&\n      document.documentElement.classList.contains(\"dark\")\n  );\n  useEffect(() => {\n    const root = document.documentElement;\n    const observer = new MutationObserver(() => {\n      setDark(root.classList.contains(\"dark\"));\n    });\n    observer.observe(root, { attributes: true, attributeFilter: [\"class\"] });\n    return () => observer.disconnect();\n  }, []);\n  return dark;\n}\n\nconst subscribeHydration = () => () => {};\n\nfunction useHydrated(): boolean {\n  return useSyncExternalStore(\n    subscribeHydration,\n    () => true,\n    () => false\n  );\n}\n\ninterface GlassProps extends ComponentProps<\"div\"> {\n  blur?: number;\n  chroma?: number;\n  depth?: number;\n  domeDepth?: number;\n  dynamicsRef?: RefObject<GlassDynamics | null>;\n  edgeExponent?: number;\n  edgeFalloff?: boolean;\n  edgeHighlight?: number;\n  edgeWidth?: number;\n  glow?: number;\n  glowExponent?: number;\n  glowSpread?: number;\n  lens: ReactNode;\n  mapSize?: number;\n  maxDisplacement?: number;\n  resolution?: number;\n  reveal?: boolean;\n  scaleX?: number;\n  scaleY?: number;\n  sdfBoundary?: boolean;\n  splay?: number;\n  specularAngle?: number;\n  specularDark?: boolean;\n  specularStrength?: number;\n  tint?: number;\n  tintBlur?: number;\n  tintColor?: string;\n}\n\ninterface SlotEls {\n  feImage: SVGFEImageElement | null;\n  feScale: SVGFEColorMatrixElement | null;\n  feFlood: SVGFEFloodElement | null;\n  feMask: SVGFEImageElement | null;\n  backdrop: HTMLDivElement | null;\n  hrefInit: boolean;\n  lastKey: string;\n  lastMaskKey: string;\n  lastMatrix: string;\n  lastX: string;\n  lastY: string;\n  lastW: string;\n  lastH: string;\n  lastWidth: number;\n  lastHeight: number;\n  lastRadius: number;\n  lastBackdrop: string;\n  prevW: number;\n  prevH: number;\n}\n\nfunction makeSlot(): SlotEls {\n  return {\n    feImage: null,\n    feScale: null,\n    feFlood: null,\n    feMask: null,\n    backdrop: null,\n    hrefInit: false,\n    lastKey: \"\",\n    lastMaskKey: \"\",\n    lastMatrix: \"\",\n    lastX: \"\",\n    lastY: \"\",\n    lastW: \"\",\n    lastH: \"\",\n    lastWidth: 0,\n    lastHeight: 0,\n    lastRadius: 0,\n    lastBackdrop: \"\",\n    prevW: 0,\n    prevH: 0,\n  };\n}\n\ninterface EngineParams {\n  depth: number;\n  domeDepth: number;\n  splay: number;\n  specularAngle: number;\n  specularStrength: number;\n  specularDark: boolean;\n  glow: number;\n  glowSpread: number;\n  glowExponent: number;\n  edgeHighlight: number;\n  edgeWidth: number;\n  edgeExponent: number;\n  mapSize: number;\n  scaleMax: number;\n  maxDisplacement: number;\n  chroma: number;\n  blur: number;\n  sdfBoundary: boolean;\n  edgeFalloff: boolean;\n  kx: number;\n  ky: number;\n  resolution: number;\n  tint: number;\n  tintBlur: number;\n  tintRGB: string;\n}\n\nconst OVERRIDE_KEYS = [\n  \"depth\",\n  \"domeDepth\",\n  \"splay\",\n  \"specularAngle\",\n  \"glow\",\n  \"glowSpread\",\n  \"glowExponent\",\n  \"edgeHighlight\",\n  \"edgeWidth\",\n  \"edgeExponent\",\n] as const;\n\ntype OverrideKey = (typeof OVERRIDE_KEYS)[number];\n\nfunction markOverrides(\n  mark: HTMLElement\n): Partial<Record<OverrideKey, number>> & { mul: number } {\n  const out: Partial<Record<OverrideKey, number>> & { mul: number } = {\n    mul: 1,\n  };\n  for (const key of OVERRIDE_KEYS) {\n    const raw = mark.dataset[`glass${key[0].toUpperCase()}${key.slice(1)}`];\n    if (raw !== undefined) {\n      const v = Number(raw);\n      if (!Number.isNaN(v)) {\n        out[key] = v;\n      }\n    }\n  }\n  const mulRaw = mark.dataset.glassMul;\n  if (mulRaw !== undefined) {\n    const v = Number(mulRaw);\n    if (!Number.isNaN(v)) {\n      out.mul = clamp01(v);\n    }\n  }\n  return out;\n}\n\nfunction axisMatrix(kx: number, ky: number, alpha = 1): string {\n  return `${kx} 0 0 0 ${(1 - kx) / 2}  0 ${ky} 0 0 ${(1 - ky) / 2}  0 0 1 0 0  0 0 0 ${alpha} 0`;\n}\n\nfunction Glass({\n  scaleX = 0.1,\n  scaleY = 0.1,\n  chroma = 0.2,\n  depth = 6,\n  domeDepth = 0,\n  splay = 1,\n  blur = 0,\n  glow = 0.12,\n  glowSpread = 1,\n  glowExponent = 1.5,\n  edgeHighlight = 0.3,\n  edgeWidth = 3,\n  edgeExponent = 1.5,\n  specularStrength = 1,\n  specularAngle = 45,\n  specularDark = false,\n  sdfBoundary = true,\n  edgeFalloff = true,\n  mapSize = 256,\n  maxDisplacement = Number.POSITIVE_INFINITY,\n  resolution = 1,\n  reveal = false,\n  tint = 0,\n  tintBlur = 12,\n  tintColor,\n  dynamicsRef,\n  lens,\n  className,\n  children,\n  ...props\n}: GlassProps) {\n  const dark = useGlassDark();\n  const tintRGB = tintColor ?? (dark ? \"58,58,62\" : \"255,255,255\");\n  const baseId = useId().replace(/[^a-zA-Z0-9-]/g, \"\");\n  const filterId = `${baseId}-glass`;\n  const contentRef = useRef<HTMLDivElement | null>(null);\n  const rootRef = useRef<HTMLDivElement | null>(null);\n  const lensLayerRef = useRef<HTMLDivElement | null>(null);\n  const filterRef = useRef<SVGFilterElement | null>(null);\n  const feBlurRef = useRef<SVGFEGaussianBlurElement | null>(null);\n  const dispRefs = useRef<(SVGFEDisplacementMapElement | null)[]>([\n    null,\n    null,\n    null,\n  ]);\n  const slotsRef = useRef<SlotEls[]>(Array.from({ length: SLOTS }, makeSlot));\n  const mapCache = useRef(new Map<string, string>());\n  const decodedUrls = useRef(new Set<string>());\n  const pendingUrls = useRef(new Set<string>());\n  const version = useRef(0);\n  const lastScale = useRef(-1);\n  const [slotCount, setSlotCount] = useState(1);\n  const slotCountRef = useRef(1);\n  slotCountRef.current = slotCount;\n\n  const scaleMax = Math.max(scaleX, scaleY, 1e-4);\n  const kx = scaleX / scaleMax;\n  const ky = scaleY / scaleMax;\n\n  const hydrated = useHydrated();\n  const rasterKind = isRasterChild(children);\n  const useWebGL =\n    hydrated &&\n    (forcedRenderer === \"svg\"\n      ? false\n      : rasterKind !== null &&\n        webglAvailable() &&\n        (forcedRenderer === \"webgl\" || isSafariBrowser()));\n\n  const params = useRef<EngineParams>({\n    depth,\n    domeDepth,\n    splay,\n    specularAngle,\n    specularStrength,\n    specularDark,\n    glow,\n    glowSpread,\n    glowExponent,\n    edgeHighlight,\n    edgeWidth,\n    edgeExponent,\n    mapSize,\n    scaleMax,\n    maxDisplacement,\n    chroma,\n    blur,\n    sdfBoundary,\n    edgeFalloff,\n    kx,\n    ky,\n    resolution,\n    tint: clamp01(tint),\n    tintBlur,\n    tintRGB,\n  });\n  params.current = {\n    depth,\n    domeDepth,\n    splay,\n    specularAngle,\n    specularStrength,\n    specularDark,\n    glow,\n    glowSpread,\n    glowExponent,\n    edgeHighlight,\n    edgeWidth,\n    edgeExponent,\n    mapSize,\n    scaleMax,\n    maxDisplacement,\n    chroma,\n    blur,\n    sdfBoundary,\n    edgeFalloff,\n    kx,\n    ky,\n    resolution,\n    tint: clamp01(tint),\n    tintBlur,\n    tintRGB,\n  };\n\n  const attach = useCallback(\n    (node: HTMLDivElement | null) => {\n      if (!node || typeof window === \"undefined\") {\n        return;\n      }\n      rootRef.current = node;\n      if (useWebGL) {\n        return;\n      }\n      let raf = 0;\n      let lastChroma = -1;\n      let lastBlurStd = -1;\n      const collapse = (slot: SlotEls) => {\n        if (slot.lastW === \"0.001\") {\n          return;\n        }\n        for (const el of [slot.feImage, slot.feFlood, slot.feMask]) {\n          el?.setAttribute(\"x\", \"0\");\n          el?.setAttribute(\"y\", \"0\");\n          el?.setAttribute(\"width\", \"0.001\");\n          el?.setAttribute(\"height\", \"0.001\");\n        }\n        if (slot.backdrop) {\n          slot.backdrop.style.display = \"none\";\n          slot.lastBackdrop = \"\";\n        }\n        slot.lastX = \"0\";\n        slot.lastY = \"0\";\n        slot.lastW = \"0.001\";\n        slot.lastH = \"0.001\";\n        slot.lastWidth = 0;\n        slot.lastHeight = 0;\n      };\n      const getMap = (key: string, mp: MapParams): string => {\n        const cached = mapCache.current.get(key);\n        if (cached !== undefined) {\n          return cached;\n        }\n        const url = generateLensMap(mp) ?? \"\";\n        mapCache.current.set(key, url);\n        if (mapCache.current.size > MAP_CACHE_LIMIT) {\n          const first = mapCache.current.keys().next().value;\n          if (first !== undefined) {\n            mapCache.current.delete(first);\n          }\n        }\n        return url;\n      };\n      const preload = (url: string): boolean => {\n        if (!url) {\n          return false;\n        }\n        if (decodedUrls.current.has(url)) {\n          return true;\n        }\n        if (pendingUrls.current.has(url)) {\n          return false;\n        }\n        pendingUrls.current.add(url);\n        const img = new Image();\n        img.onload = () => {\n          pendingUrls.current.delete(url);\n          if (decodedUrls.current.size > 256) {\n            decodedUrls.current.clear();\n          }\n          decodedUrls.current.add(url);\n        };\n        img.onerror = () => {\n          pendingUrls.current.delete(url);\n        };\n        img.src = url;\n        return false;\n      };\n      const tick = () => {\n        const layer = lensLayerRef.current;\n        for (const slot of slotsRef.current) {\n          if (!slot.hrefInit && slot.feImage) {\n            if (!slot.feImage.getAttribute(\"href\")) {\n              slot.feImage.setAttribute(\"href\", placeholderHref());\n            }\n            slot.hrefInit = true;\n          }\n        }\n        const p = params.current;\n        if (p.chroma !== lastChroma) {\n          lastChroma = p.chroma;\n          lastScale.current = -1;\n        }\n        const dyn = dynamicsRef?.current ?? undefined;\n        const zoom = dyn?.zoom ?? 1;\n        const depthMul = dyn?.depthMul ?? 1;\n        const G = p.resolution;\n        let mapChanged = false;\n        let rectChanged = false;\n        let hasLens = false;\n        let activeCount = 0;\n        let base: DOMRect | null = null;\n        if (layer) {\n          base = node.getBoundingClientRect();\n          const marks = layer.querySelectorAll<HTMLElement>(\"[data-glass-lens]\");\n          for (let i = 0; i < SLOTS; i++) {\n            const slot = slotsRef.current[i];\n            const mark = marks[i];\n            if (!mark) {\n              collapse(slot);\n              continue;\n            }\n            const r = mark.getBoundingClientRect();\n            if (r.width < 2 || r.height < 2) {\n              collapse(slot);\n              continue;\n            }\n            hasLens = true;\n            activeCount += 1;\n            const ov = markOverrides(mark);\n            const blend = mark.dataset.glassBlend === \"1\";\n            const tintRaw = mark.dataset.glassTint;\n            const tintNum = tintRaw === undefined ? Number.NaN : Number(tintRaw);\n            const lensTint = Number.isNaN(tintNum) ? p.tint : clamp01(tintNum);\n            const tintEff = 0.5 * lensTint;\n            const md = i === 0 ? dyn?.mapDims : undefined;\n            const rectW = Math.round(r.width * 2) / 2;\n            const rectH = Math.round(r.height * 2) / 2;\n            const genW = md ? Math.round(md.halfW * 4) / 2 : rectW;\n            const genH = md ? Math.round(md.halfH * 4) / 2 : rectH;\n            const resizing =\n              Math.abs(r.width - slot.prevW) > 0.3 ||\n              Math.abs(r.height - slot.prevH) > 0.3;\n            slot.prevW = r.width;\n            slot.prevH = r.height;\n            const radiusParts = getComputedStyle(mark)\n              .borderTopLeftRadius.split(\" \")\n              .map((v) => Math.round(Number.parseFloat(v) * 2) / 2 || 0);\n            const cssRadius = radiusParts[0] ?? 0;\n            const cssRadiusY = radiusParts[1] ?? cssRadius;\n            const radius = md ? Math.round(md.radius * 2) / 2 : cssRadius;\n            const sizeChanged =\n              Math.abs(genW - slot.lastWidth) >= 0.5 ||\n              Math.abs(genH - slot.lastHeight) >= 0.5 ||\n              Math.abs(radius - slot.lastRadius) >= 0.5;\n            const unitsPerPx = md ? genW / Math.max(rectW, 1) : 1;\n            const baseDepth = ov.depth ?? p.depth;\n            const baseDome = ov.domeDepth ?? p.domeDepth;\n            const baseEdgeW = ov.edgeWidth ?? p.edgeWidth;\n            const effDepth =\n              Math.round(baseDepth * depthMul * unitsPerPx * 10) / 10;\n            const effDome = Math.round(baseDome * unitsPerPx * 10) / 10;\n            const effEdgeW = Math.round(baseEdgeW * unitsPerPx * 10) / 10;\n            const splayV = ov.splay ?? p.splay;\n            const angleV = ov.specularAngle ?? p.specularAngle;\n            const glowV = ov.glow ?? p.glow;\n            const glowSpreadV = ov.glowSpread ?? p.glowSpread;\n            const glowExpV = ov.glowExponent ?? p.glowExponent;\n            const edgeV = ov.edgeHighlight ?? p.edgeHighlight;\n            const edgeExpV = ov.edgeExponent ?? p.edgeExponent;\n            const key = `${genW}x${genH}r${radius}d${effDepth}o${effDome}p${splayV}a${angleV}g${glowV},${glowSpreadV},${glowExpV}e${edgeV},${effEdgeW},${edgeExpV}m${p.mapSize}f${p.sdfBoundary ? 1 : 0}${p.edgeFalloff ? 1 : 0}${blend ? 1 : 0}`;\n            if (!slot.lastKey || (!resizing && (sizeChanged || key !== slot.lastKey))) {\n              const url = getMap(key, {\n                halfW: genW / 2,\n                halfH: genH / 2,\n                radius,\n                depth: effDepth,\n                domeDepth: effDome,\n                splay: splayV,\n                specularAngle: angleV,\n                glowStrength: glowV,\n                glowSpread: glowSpreadV,\n                glowExponent: glowExpV,\n                edgeStrength: edgeV,\n                edgeWidth: effEdgeW,\n                edgeExponent: edgeExpV,\n                size: p.mapSize,\n                sdfBoundary: p.sdfBoundary,\n                edgeFalloff: p.edgeFalloff,\n                alphaBlend: blend,\n              });\n              if (preload(url)) {\n                slot.feImage?.setAttribute(\"href\", url);\n                slot.lastKey = key;\n                slot.lastWidth = genW;\n                slot.lastHeight = genH;\n                slot.lastRadius = radius;\n                mapChanged = true;\n              }\n            }\n            const tintFade = 1 - 0.85 * tintEff;\n            const matrix = axisMatrix(\n              p.kx * ov.mul * tintFade,\n              p.ky * ov.mul * tintFade,\n              ov.mul > 0 ? 1 : 0\n            );\n            if (slot.feScale && slot.lastMatrix !== matrix) {\n              slot.feScale.setAttribute(\"values\", matrix);\n              slot.lastMatrix = matrix;\n              rectChanged = true;\n            }\n            const fx = Math.round((r.left - base.left) * 2) / 2;\n            const fy = Math.round((r.top - base.top) * 2) / 2;\n            const bw = base.width || 1;\n            const bh = base.height || 1;\n            const sx = String((fx + EDGE_BIAS) / bw);\n            const sy = String((fy + EDGE_BIAS) / bh);\n            const sw = String(Math.max(0, rectW - 2 * EDGE_BIAS) / bw);\n            const sh = String(Math.max(0, rectH - 2 * EDGE_BIAS) / bh);\n            if (\n              slot.feImage &&\n              (slot.lastX !== sx ||\n                slot.lastY !== sy ||\n                slot.lastW !== sw ||\n                slot.lastH !== sh)\n            ) {\n              for (const el of [slot.feImage, slot.feFlood, slot.feMask]) {\n                el?.setAttribute(\"x\", sx);\n                el?.setAttribute(\"y\", sy);\n                el?.setAttribute(\"width\", sw);\n                el?.setAttribute(\"height\", sh);\n              }\n              slot.lastX = sx;\n              slot.lastY = sy;\n              slot.lastW = sw;\n              slot.lastH = sh;\n              rectChanged = true;\n            }\n            if (slot.feMask) {\n              const maskKey = `${rectW}|${rectH}|${cssRadius}|${cssRadiusY}|${G}`;\n              if (!slot.lastMaskKey || (!resizing && slot.lastMaskKey !== maskKey)) {\n                const maskUrl = roundedMaskHref(\n                  (rectW - 2 * EDGE_BIAS) * G,\n                  (rectH - 2 * EDGE_BIAS) * G,\n                  cssRadius * G,\n                  cssRadiusY * G\n                );\n                if (preload(maskUrl)) {\n                  slot.feMask.setAttribute(\"href\", maskUrl);\n                  slot.lastMaskKey = maskKey;\n                  mapChanged = true;\n                }\n              }\n            }\n            const bd = slot.backdrop;\n            if (bd) {\n              const blurPx = Math.max(p.blur, tintEff * p.tintBlur);\n              const filter =\n                blurPx > 0.05\n                  ? `blur(${blurPx}px) saturate(${1 + 0.5 * tintEff})`\n                  : \"none\";\n              const bg =\n                tintEff > 0.001\n                  ? `rgba(${p.tintRGB},${Math.round(tintEff * 700) / 1000})`\n                  : \"transparent\";\n              const bdKey = `${fx},${fy},${rectW},${rectH},${cssRadius},${filter},${bg}`;\n              if (slot.lastBackdrop !== bdKey) {\n                bd.style.transform = `translate3d(${fx}px, ${fy}px, 0)`;\n                bd.style.width = `${rectW}px`;\n                bd.style.height = `${rectH}px`;\n                bd.style.borderRadius = `${cssRadius}px`;\n                bd.style.backdropFilter = filter;\n                bd.style.setProperty(\"-webkit-backdrop-filter\", filter);\n                bd.style.background = bg;\n                bd.style.display = \"block\";\n                slot.lastBackdrop = bdKey;\n              }\n            }\n          }\n        }\n        const desiredSlots = Math.min(Math.max(activeCount, 1), SLOTS);\n        if (desiredSlots !== slotCountRef.current) {\n          slotCountRef.current = desiredSlots;\n          setSlotCount(desiredSlots);\n        }\n        if (hasLens && base) {\n          const blurStd = p.blur / (base.width || 1);\n          if (blurStd !== lastBlurStd) {\n            lastBlurStd = blurStd;\n            feBlurRef.current?.setAttribute(\"stdDeviation\", String(blurStd));\n          }\n          const dispScale = Math.min(\n            p.scaleMax * zoom,\n            p.maxDisplacement / (base.width || 1)\n          );\n          if (Math.abs(dispScale - lastScale.current) > 0.0001) {\n            lastScale.current = dispScale;\n            const c = p.chroma;\n            const scales =\n              c > 0\n                ? [dispScale * (1 + 0.2 * c), dispScale * (1 + 0.1 * c), dispScale]\n                : [dispScale];\n            dispRefs.current.forEach((el, i) => {\n              if (el && scales[i] !== undefined) {\n                el.setAttribute(\"scale\", String(scales[i]));\n              }\n            });\n            rectChanged = true;\n          }\n        }\n        if (\n          (mapChanged || (isWebKitOnly && rectChanged)) &&\n          filterRef.current &&\n          contentRef.current\n        ) {\n          version.current += 1;\n          const id = `${filterId}-v${version.current}`;\n          filterRef.current.id = id;\n          contentRef.current.style.filter = `url(#${id})`;\n        }\n        raf = requestAnimationFrame(tick);\n      };\n      raf = requestAnimationFrame(tick);\n      return () => cancelAnimationFrame(raf);\n    },\n    [filterId, dynamicsRef, useWebGL]\n  );\n\n  useEffect(() => {\n    if (!useWebGL || typeof window === \"undefined\") {\n      return;\n    }\n    const node = rootRef.current;\n    const lensLayer = lensLayerRef.current;\n    const sourceEl =\n      contentRef.current?.querySelector<\n        HTMLVideoElement | HTMLCanvasElement | HTMLImageElement\n      >(\"video,canvas,img\") ?? null;\n    if (!node || !lensLayer || !sourceEl) {\n      return;\n    }\n    const renderer = new GlassWebGLRenderer();\n    return renderer.attach(node, lensLayer, sourceEl, {\n      getParams: () => params.current,\n      getDynamics: () => dynamicsRef?.current ?? undefined,\n      reveal,\n    });\n  }, [useWebGL, reveal, dynamicsRef]);\n\n  const source = blur > 0 ? \"blurred\" : \"SourceGraphic\";\n  const slotIndexes = Array.from({ length: SLOTS }, (_, i) => i);\n  const renderSlots = Array.from({ length: slotCount }, (_, i) => i);\n  const axisValues = axisMatrix(kx, ky);\n\n  return (\n    <div className={cn(\"relative\", className)} ref={attach} {...props}>\n      <div\n        className=\"relative h-full w-full\"\n        ref={contentRef}\n        style={{\n          ...(useWebGL\n            ? { visibility: reveal ? \"hidden\" : \"visible\" }\n            : { filter: `url(#${filterId})`, willChange: \"filter\" }),\n          ...(resolution !== 1\n            ? {\n                width: `${resolution * 100}%`,\n                height: `${resolution * 100}%`,\n                transform: `scale(${1 / resolution})`,\n                transformOrigin: \"0 0\",\n              }\n            : null),\n        }}\n      >\n        {resolution !== 1 ? (\n          <div\n            style={{\n              width: `${100 / resolution}%`,\n              height: `${100 / resolution}%`,\n              transform: `scale(${resolution})`,\n              transformOrigin: \"0 0\",\n            }}\n          >\n            {children}\n          </div>\n        ) : (\n          children\n        )}\n      </div>\n      <div aria-hidden=\"true\" className=\"pointer-events-none absolute inset-0\">\n        {slotIndexes.map((i) => (\n          <div\n            key={`bd-${i}`}\n            ref={(el) => {\n              slotsRef.current[i].backdrop = el;\n            }}\n            style={{\n              position: \"absolute\",\n              top: 0,\n              left: 0,\n              display: \"none\",\n              willChange: \"backdrop-filter, transform\",\n            }}\n          />\n        ))}\n      </div>\n      <div\n        aria-hidden=\"true\"\n        className=\"pointer-events-none absolute inset-0\"\n        ref={lensLayerRef}\n      >\n        {lens}\n      </div>\n      {!useWebGL && (\n        <svg aria-hidden=\"true\" className=\"absolute h-0 w-0\" focusable=\"false\">\n          <defs>\n          <filter\n            colorInterpolationFilters=\"sRGB\"\n            filterUnits=\"objectBoundingBox\"\n            height=\"1\"\n            id={filterId}\n            primitiveUnits=\"objectBoundingBox\"\n            ref={filterRef}\n            width=\"1\"\n            x=\"0\"\n            y=\"0\"\n          >\n            <feFlood\n              floodColor=\"rgb(128,128,128)\"\n              floodOpacity=\"1\"\n              result=\"mapBg\"\n            />\n            {renderSlots.map((i) => (\n              <feImage\n                key={`img-${i}`}\n                preserveAspectRatio=\"none\"\n                ref={(el) => {\n                  slotsRef.current[i].feImage = el;\n                }}\n                result={`raw${i}`}\n              />\n            ))}\n            {renderSlots.map((i) => (\n              <feColorMatrix\n                in={`raw${i}`}\n                key={`scale-${i}`}\n                ref={(el) => {\n                  slotsRef.current[i].feScale = el;\n                }}\n                result={`scaled${i}`}\n                type=\"matrix\"\n                values={axisValues}\n              />\n            ))}\n            {renderSlots.map((i) => (\n              <feComposite\n                in={`scaled${i}`}\n                in2={i === 0 ? \"mapBg\" : `m${i - 1}`}\n                key={`mc-${i}`}\n                operator=\"over\"\n                result={i === slotCount - 1 ? \"map\" : `m${i}`}\n              />\n            ))}\n            {blur > 0 && (\n              <feGaussianBlur\n                in=\"SourceGraphic\"\n                ref={feBlurRef}\n                result=\"blurred\"\n                stdDeviation=\"0\"\n              />\n            )}\n            {chroma > 0 ? (\n              <>\n                <feDisplacementMap\n                  in={source}\n                  in2=\"map\"\n                  ref={(el) => {\n                    dispRefs.current[0] = el;\n                    if (el && !el.hasAttribute(\"scale\")) {\n                      el.setAttribute(\"scale\", \"0\");\n                      lastScale.current = -1;\n                    }\n                  }}\n                  result=\"dR\"\n                  xChannelSelector=\"R\"\n                  yChannelSelector=\"G\"\n                />\n                <feColorMatrix\n                  in=\"dR\"\n                  result=\"cR\"\n                  type=\"matrix\"\n                  values=\"1 0 0 0 0  0 0 0 0 0  0 0 0 0 0  0 0 0 1 0\"\n                />\n                <feDisplacementMap\n                  in={source}\n                  in2=\"map\"\n                  ref={(el) => {\n                    dispRefs.current[1] = el;\n                    if (el && !el.hasAttribute(\"scale\")) {\n                      el.setAttribute(\"scale\", \"0\");\n                      lastScale.current = -1;\n                    }\n                  }}\n                  result=\"dG\"\n                  xChannelSelector=\"R\"\n                  yChannelSelector=\"G\"\n                />\n                <feColorMatrix\n                  in=\"dG\"\n                  result=\"cG\"\n                  type=\"matrix\"\n                  values=\"0 0 0 0 0  0 1 0 0 0  0 0 0 0 0  0 0 0 1 0\"\n                />\n                <feDisplacementMap\n                  in={source}\n                  in2=\"map\"\n                  ref={(el) => {\n                    dispRefs.current[2] = el;\n                    if (el && !el.hasAttribute(\"scale\")) {\n                      el.setAttribute(\"scale\", \"0\");\n                      lastScale.current = -1;\n                    }\n                  }}\n                  result=\"dB\"\n                  xChannelSelector=\"R\"\n                  yChannelSelector=\"G\"\n                />\n                <feColorMatrix\n                  in=\"dB\"\n                  result=\"cB\"\n                  type=\"matrix\"\n                  values=\"0 0 0 0 0  0 0 0 0 0  0 0 1 0 0  0 0 0 1 0\"\n                />\n                <feComposite\n                  in=\"cR\"\n                  in2=\"cG\"\n                  k1=\"0\"\n                  k2=\"1\"\n                  k3=\"1\"\n                  k4=\"0\"\n                  operator=\"arithmetic\"\n                  result=\"cRG\"\n                />\n                <feComposite\n                  in=\"cRG\"\n                  in2=\"cB\"\n                  k1=\"0\"\n                  k2=\"1\"\n                  k3=\"1\"\n                  k4=\"0\"\n                  operator=\"arithmetic\"\n                  result=\"lensResult\"\n                />\n              </>\n            ) : (\n              <feDisplacementMap\n                in={source}\n                in2=\"map\"\n                ref={(el) => {\n                  dispRefs.current[0] = el;\n                  if (el && !el.hasAttribute(\"scale\")) {\n                    el.setAttribute(\"scale\", \"0\");\n                    lastScale.current = -1;\n                  }\n                }}\n                result=\"lensResult\"\n                xChannelSelector=\"R\"\n                yChannelSelector=\"G\"\n              />\n            )}\n            {(glow > 0 || edgeHighlight > 0) &&\n              (specularDark ? (\n                <>\n                  <feColorMatrix\n                    in=\"map\"\n                    result=\"spec\"\n                    type=\"matrix\"\n                    values={`0 0 ${-specularStrength} 0 ${1 + (128 * specularStrength) / 255}  0 0 ${-specularStrength} 0 ${1 + (128 * specularStrength) / 255}  0 0 ${-specularStrength} 0 ${1 + (128 * specularStrength) / 255}  0 0 0 0 1`}\n                  />\n                  <feComposite\n                    in=\"spec\"\n                    in2=\"lensResult\"\n                    k1=\"1\"\n                    k2=\"0\"\n                    k3=\"0\"\n                    k4=\"0\"\n                    operator=\"arithmetic\"\n                    result=\"lensResult\"\n                  />\n                </>\n              ) : (\n                <>\n                  <feColorMatrix\n                    in=\"map\"\n                    result=\"spec\"\n                    type=\"matrix\"\n                    values=\"0 0 0 0 1  0 0 0 0 1  0 0 0 0 1  0 0 1 0 -0.5019607843\"\n                  />\n                  <feComposite\n                    in=\"spec\"\n                    in2=\"lensResult\"\n                    k1=\"0\"\n                    k2={specularStrength}\n                    k3=\"1\"\n                    k4=\"0\"\n                    operator=\"arithmetic\"\n                    result=\"lensResult\"\n                  />\n                </>\n              ))}\n            {renderSlots.map((i) => (\n              <feImage\n                height=\"0.001\"\n                key={`mask-${i}`}\n                preserveAspectRatio=\"none\"\n                ref={(el) => {\n                  slotsRef.current[i].feMask = el;\n                }}\n                result={`mask${i}`}\n                width=\"0.001\"\n                x=\"0\"\n                y=\"0\"\n              />\n            ))}\n            <feMerge result=\"unionMask\">\n              {renderSlots.map((i) => (\n                <feMergeNode in={`mask${i}`} key={`mn-${i}`} />\n              ))}\n            </feMerge>\n            {reveal ? (\n              <feComposite in=\"lensResult\" in2=\"unionMask\" operator=\"in\" />\n            ) : (\n              <>\n                <feComposite\n                  in=\"lensResult\"\n                  in2=\"unionMask\"\n                  operator=\"in\"\n                  result=\"lensClipped\"\n                />\n                <feComposite\n                  in=\"SourceGraphic\"\n                  in2=\"unionMask\"\n                  operator=\"out\"\n                  result=\"holedSG\"\n                />\n                <feComposite\n                  in=\"lensClipped\"\n                  in2=\"holedSG\"\n                  k1=\"0\"\n                  k2=\"1\"\n                  k3=\"1\"\n                  k4=\"0\"\n                  operator=\"arithmetic\"\n                />\n              </>\n            )}\n          </filter>\n        </defs>\n        </svg>\n      )}\n    </div>\n  );\n}\n\nexport { Glass, generateLensMap, isSafariBrowser, useGlassDark, useHydrated };\nexport type { GlassDynamics, MapParams };\n",
      "type": "registry:ui"
    },
    {
      "path": "registry/default/glass/glass-motion.ts",
      "content": "type Subscriber = (value: number) => void;\n\nconst VELOCITY_RESET_MS = 80;\n\nclass MotionValue {\n  private current: number;\n  private prevTime = 0;\n  private velocityValue = 0;\n  private subs = new Set<Subscriber>();\n  private cancelFn: (() => void) | null = null;\n\n  constructor(initial: number) {\n    this.current = initial;\n  }\n\n  get(): number {\n    return this.current;\n  }\n\n  getVelocity(): number {\n    if (performance.now() - this.prevTime > VELOCITY_RESET_MS) {\n      return 0;\n    }\n    return this.velocityValue;\n  }\n\n  set(next: number, timestamp = performance.now()) {\n    const elapsed = timestamp - this.prevTime;\n    if (elapsed > VELOCITY_RESET_MS) {\n      this.velocityValue = 0;\n    } else if (elapsed > 0) {\n      const dt = Math.min(Math.max(elapsed, 8), 30);\n      this.velocityValue = ((next - this.current) / dt) * 1000;\n    }\n    this.prevTime = timestamp;\n    this.current = next;\n    for (const fn of this.subs) {\n      fn(next);\n    }\n  }\n\n  jump(next: number) {\n    this.stop();\n    this.velocityValue = 0;\n    this.prevTime = performance.now();\n    this.current = next;\n    for (const fn of this.subs) {\n      fn(next);\n    }\n  }\n\n  on(fn: Subscriber): () => void {\n    this.subs.add(fn);\n    return () => this.subs.delete(fn);\n  }\n\n  setCancel(fn: (() => void) | null) {\n    this.cancelFn = fn;\n  }\n\n  stop() {\n    this.cancelFn?.();\n    this.cancelFn = null;\n  }\n}\n\nfunction cubicBezier(x1: number, y1: number, x2: number, y2: number) {\n  const cx = 3 * x1;\n  const bx = 3 * (x2 - x1) - cx;\n  const ax = 1 - cx - bx;\n  const cy = 3 * y1;\n  const by = 3 * (y2 - y1) - cy;\n  const ay = 1 - cy - by;\n  const sampleX = (t: number) => ((ax * t + bx) * t + cx) * t;\n  const sampleY = (t: number) => ((ay * t + by) * t + cy) * t;\n  const sampleDX = (t: number) => (3 * ax * t + 2 * bx) * t + cx;\n  return (x: number) => {\n    if (x <= 0) {\n      return 0;\n    }\n    if (x >= 1) {\n      return 1;\n    }\n    let t = x;\n    for (let i = 0; i < 8; i++) {\n      const err = sampleX(t) - x;\n      if (Math.abs(err) < 1e-6) {\n        return sampleY(t);\n      }\n      const d = sampleDX(t);\n      if (Math.abs(d) < 1e-6) {\n        break;\n      }\n      t -= err / d;\n    }\n    let lo = 0;\n    let hi = 1;\n    t = x;\n    while (hi - lo > 1e-6) {\n      if (sampleX(t) < x) {\n        lo = t;\n      } else {\n        hi = t;\n      }\n      t = (lo + hi) / 2;\n    }\n    return sampleY(t);\n  };\n}\n\nconst glassEase = cubicBezier(0.22, 1.15, 0.36, 1.06);\n\nconst PRESS_DURATION = 0.32;\nconst RELEASE_DURATION = 0.52;\nconst TRAVEL_DURATION = 0.6;\n\nfunction tween(\n  value: MotionValue,\n  to: number,\n  duration: number,\n  ease: (t: number) => number,\n  onComplete?: () => void\n): () => void {\n  value.stop();\n  if (duration <= 0) {\n    value.jump(to);\n    onComplete?.();\n    return () => undefined;\n  }\n  const from = value.get();\n  const start = performance.now();\n  let raf = 0;\n  const step = (now: number) => {\n    const t = Math.min((now - start) / (duration * 1000), 1);\n    value.set(from + (to - from) * ease(t), now);\n    if (t < 1) {\n      raf = requestAnimationFrame(step);\n    } else {\n      value.setCancel(null);\n      onComplete?.();\n    }\n  };\n  raf = requestAnimationFrame(step);\n  const cancel = () => cancelAnimationFrame(raf);\n  value.setCancel(cancel);\n  return cancel;\n}\n\ninterface SpringOptions {\n  stiffness: number;\n  damping: number;\n  restDelta?: number;\n  restSpeed?: number;\n  onSettle?: () => void;\n}\n\nclass SpringDriver {\n  private vel = 0;\n  private raf = 0;\n  private last = 0;\n  private running = false;\n  private readonly restDelta: number;\n  private readonly restSpeed: number;\n\n  constructor(\n    private readonly value: MotionValue,\n    private readonly options: SpringOptions,\n    private readonly getTarget: () => number,\n    private readonly canRest: () => boolean = () => true\n  ) {\n    this.restDelta = options.restDelta ?? 5e-4;\n    this.restSpeed = options.restSpeed ?? 0.005;\n  }\n\n  start() {\n    if (this.running) {\n      return;\n    }\n    this.value.stop();\n    this.value.setCancel(() => this.stop());\n    this.running = true;\n    this.last = performance.now();\n    const step = (now: number) => {\n      if (!this.running) {\n        return;\n      }\n      const dt = Math.min((now - this.last) / 1000, 0.033);\n      this.last = now;\n      const target = this.getTarget();\n      const x = this.value.get();\n      const accel =\n        -this.options.stiffness * (x - target) - this.options.damping * this.vel;\n      this.vel += accel * dt;\n      const next = x + this.vel * dt;\n      this.value.set(next, now);\n      const settled =\n        Math.abs(next - target) < this.restDelta &&\n        Math.abs(this.vel) < this.restSpeed;\n      if (settled && this.canRest()) {\n        this.running = false;\n        this.vel = 0;\n        this.value.setCancel(null);\n        this.value.set(target, now);\n        this.options.onSettle?.();\n        return;\n      }\n      this.raf = requestAnimationFrame(step);\n    };\n    this.raf = requestAnimationFrame(step);\n  }\n\n  stop() {\n    this.running = false;\n    this.vel = 0;\n    cancelAnimationFrame(this.raf);\n  }\n}\n\nfunction rubberBand(distance: number, overshoot: number, dampening: number): number {\n  if (overshoot <= 0 || distance === 0) {\n    return 0;\n  }\n  const range = overshoot * dampening;\n  const t = Math.min(1, Math.abs(distance) / range);\n  return Math.sign(distance) * overshoot * (1 - (1 - t) ** 3);\n}\n\nfunction prefersReducedMotion(): boolean {\n  return (\n    typeof window !== \"undefined\" &&\n    window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches\n  );\n}\n\nexport {\n  cubicBezier,\n  glassEase,\n  MotionValue,\n  PRESS_DURATION,\n  prefersReducedMotion,\n  RELEASE_DURATION,\n  rubberBand,\n  SpringDriver,\n  TRAVEL_DURATION,\n  tween,\n};\n",
      "type": "registry:ui"
    },
    {
      "path": "registry/default/glass/glass-surface.tsx",
      "content": "\"use client\";\n\nimport {\n  generateLensMap,\n  isSafariBrowser,\n  useGlassDark,\n  useHydrated,\n} from \"@/components/ui/glass\";\nimport { cn } from \"@/lib/utils\";\nimport {\n  type ComponentProps,\n  type RefObject,\n  useEffect,\n  useId,\n  useImperativeHandle,\n  useRef,\n  useState,\n} from \"react\";\n\nconst TINT_MAX_ALPHA = 0.4;\nconst MIN_VEIL = 0.07;\nconst MIN_BLUR = 2.5;\nconst MAP_SIZE = 256;\nconst MAP_CACHE_LIMIT = 48;\nconst GLOW = 0.16;\nconst EDGE = 0.85;\nconst SPECULAR_STRENGTH = 1.7;\n\nconst surfaceMapCache = new Map<string, string>();\n\nfunction clamp01(v: number): number {\n  return v < 0 ? 0 : v > 1 ? 1 : v;\n}\n\nfunction refractionSupported(): boolean {\n  if (typeof navigator === \"undefined\") {\n    return false;\n  }\n  return (\n    /(Chrome|Chromium|Edg|OPR)\\//.test(navigator.userAgent) &&\n    !isSafariBrowser()\n  );\n}\n\nfunction surfaceMap(\n  w: number,\n  h: number,\n  radius: number,\n  rim: number\n): string {\n  const key = `${w}|${h}|${radius}|${rim}`;\n  const cached = surfaceMapCache.get(key);\n  if (cached !== undefined) {\n    return cached;\n  }\n  const url =\n    generateLensMap({\n      halfW: w / 2,\n      halfH: h / 2,\n      radius,\n      depth: rim,\n      domeDepth: 0,\n      splay: 1,\n      specularAngle: 45,\n      glowStrength: GLOW,\n      glowSpread: 1,\n      glowExponent: 1.5,\n      edgeStrength: EDGE,\n      edgeWidth: 2,\n      edgeExponent: 1.5,\n      size: MAP_SIZE,\n      sdfBoundary: true,\n      edgeFalloff: true,\n    }) ?? \"\";\n  surfaceMapCache.set(key, url);\n  if (surfaceMapCache.size > MAP_CACHE_LIMIT) {\n    const first = surfaceMapCache.keys().next().value;\n    if (first !== undefined) {\n      surfaceMapCache.delete(first);\n    }\n  }\n  return url;\n}\n\ninterface GlassSurfaceHandle {\n  setTintLift(delta: number): void;\n}\n\ninterface GlassSurfaceProps extends ComponentProps<\"div\"> {\n  blur?: number;\n  chroma?: number;\n  handleRef?: RefObject<GlassSurfaceHandle | null>;\n  radius?: number;\n  saturation?: number;\n  specular?: boolean;\n  tint?: number;\n  tintColor?: string;\n}\n\nfunction GlassSurface({\n  blur = 12,\n  chroma = 0,\n  handleRef,\n  radius = 16,\n  saturation = 1.5,\n  specular = true,\n  tint = 0.5,\n  tintColor,\n  className,\n  style,\n  children,\n  ...props\n}: GlassSurfaceProps) {\n  const dark = useGlassDark();\n  const t = clamp01(tint);\n  const tintRGB = tintColor ?? (dark ? \"58,58,62\" : \"255,255,255\");\n  const tintRef = useRef<HTMLDivElement | null>(null);\n  const rootRef = useRef<HTMLDivElement | null>(null);\n  const feImageRef = useRef<SVGFEImageElement | null>(null);\n  const dispRefs = useRef<(SVGFEDisplacementMapElement | null)[]>([\n    null,\n    null,\n    null,\n  ]);\n  const baseId = useId().replace(/[^a-zA-Z0-9-]/g, \"\");\n  const filterId = `${baseId}-surface`;\n  const [supported] = useState(refractionSupported);\n  const refract = useHydrated() && supported;\n  const chromaOn = chroma > 0;\n\n  const blurPx = Math.max(t * blur * 0.5, MIN_BLUR);\n  const dispFade = 1 - 0.3 * t;\n\n  useImperativeHandle(\n    handleRef,\n    () => ({\n      setTintLift(delta: number) {\n        if (tintRef.current) {\n          tintRef.current.style.background = `rgba(${tintRGB},${Math.max(clamp01(t + delta) * TINT_MAX_ALPHA, MIN_VEIL)})`;\n        }\n      },\n    }),\n    [t, tintRGB]\n  );\n\n  useEffect(() => {\n    if (!refract) {\n      return;\n    }\n    const node = rootRef.current;\n    if (!node) {\n      return;\n    }\n    let lastKey = \"\";\n    const update = () => {\n      const w = node.offsetWidth;\n      const h = node.offsetHeight;\n      if (w < 4 || h < 4) {\n        return;\n      }\n      const half = Math.min(w, h) / 2;\n      const r = Math.max(0, Math.min(radius, w / 2, h / 2));\n      const rim = Math.max(6, Math.min(0.18 * Math.min(w, h), 24, half - 2));\n      const key = `${w}|${h}|${r}|${rim}`;\n      if (key !== lastKey) {\n        lastKey = key;\n        const img = feImageRef.current;\n        if (img) {\n          img.setAttribute(\"href\", surfaceMap(w, h, r, rim));\n          img.setAttribute(\"width\", String(w));\n          img.setAttribute(\"height\", String(h));\n        }\n      }\n      const base = Math.min(0.17 * Math.min(w, h), 26) * dispFade;\n      const scales = chromaOn\n        ? [base * (1 + 0.22 * chroma), base * (1 + 0.11 * chroma), base]\n        : [base, base, base];\n      dispRefs.current.forEach((el, i) => {\n        el?.setAttribute(\"scale\", String(scales[i] ?? base));\n      });\n    };\n    const observer = new ResizeObserver(update);\n    observer.observe(node);\n    update();\n    return () => observer.disconnect();\n  }, [refract, radius, chroma, chromaOn, dispFade]);\n\n  const filterParts: string[] = [];\n  if (refract) {\n    filterParts.push(`url(#${filterId})`);\n  }\n  if (blurPx > 0.05) {\n    filterParts.push(`blur(${blurPx}px)`);\n  }\n  const sat = 1 + (saturation - 1) * t;\n  if (sat > 1.001) {\n    filterParts.push(`saturate(${sat})`);\n  }\n  const backdropFilter = filterParts.length ? filterParts.join(\" \") : \"none\";\n\n  const rim = dark\n    ? \"inset 0 1px 1px 0 rgba(255,255,255,0.55), inset 0 0 0 1px rgba(255,255,255,0.2), inset 0 -12px 24px -16px rgba(255,255,255,0.4), 0 18px 44px -12px rgba(0,0,0,0.55)\"\n    : \"inset 0 1px 1px 0 rgba(255,255,255,0.95), inset 0 0 0 1px rgba(255,255,255,0.45), inset 0 -12px 24px -16px rgba(255,255,255,0.6), 0 16px 40px -10px rgba(0,0,0,0.22)\";\n  const sheen = dark\n    ? \"linear-gradient(135deg, rgba(255,255,255,0.16) 0%, rgba(255,255,255,0) 32%, rgba(255,255,255,0) 68%, rgba(255,255,255,0.07) 100%)\"\n    : \"linear-gradient(135deg, rgba(255,255,255,0.28) 0%, rgba(255,255,255,0) 34%, rgba(255,255,255,0) 66%, rgba(255,255,255,0.14) 100%)\";\n\n  return (\n    <div\n      className={cn(\"relative\", className)}\n      ref={rootRef}\n      style={{ borderRadius: radius, ...style }}\n      {...props}\n    >\n      <div\n        aria-hidden=\"true\"\n        className=\"pointer-events-none absolute inset-0 rounded-[inherit]\"\n        style={{\n          backdropFilter,\n          WebkitBackdropFilter: backdropFilter,\n        }}\n      />\n      <div\n        aria-hidden=\"true\"\n        className=\"pointer-events-none absolute inset-0 rounded-[inherit]\"\n        ref={tintRef}\n        style={{\n          background: `rgba(${tintRGB},${Math.max(t * TINT_MAX_ALPHA, MIN_VEIL)})`,\n        }}\n      />\n      {specular ? (\n        <>\n          <div\n            aria-hidden=\"true\"\n            className=\"pointer-events-none absolute inset-0 rounded-[inherit]\"\n            style={{ background: sheen }}\n          />\n          <div\n            aria-hidden=\"true\"\n            className=\"pointer-events-none absolute inset-0 rounded-[inherit]\"\n            style={{ boxShadow: rim }}\n          />\n        </>\n      ) : null}\n      {refract ? (\n        <svg aria-hidden=\"true\" className=\"absolute h-0 w-0\" focusable=\"false\">\n          <defs>\n            <filter\n              colorInterpolationFilters=\"sRGB\"\n              filterUnits=\"objectBoundingBox\"\n              height=\"1\"\n              id={filterId}\n              primitiveUnits=\"userSpaceOnUse\"\n              width=\"1\"\n              x=\"0\"\n              y=\"0\"\n            >\n              <feImage\n                preserveAspectRatio=\"none\"\n                ref={feImageRef}\n                result=\"map\"\n                x=\"0\"\n                y=\"0\"\n              />\n              {chromaOn ? (\n                <>\n                  <feDisplacementMap\n                    in=\"SourceGraphic\"\n                    in2=\"map\"\n                    ref={(el) => {\n                      dispRefs.current[0] = el;\n                    }}\n                    result=\"dR\"\n                    scale=\"0\"\n                    xChannelSelector=\"R\"\n                    yChannelSelector=\"G\"\n                  />\n                  <feColorMatrix\n                    in=\"dR\"\n                    result=\"cR\"\n                    type=\"matrix\"\n                    values=\"1 0 0 0 0  0 0 0 0 0  0 0 0 0 0  0 0 0 1 0\"\n                  />\n                  <feDisplacementMap\n                    in=\"SourceGraphic\"\n                    in2=\"map\"\n                    ref={(el) => {\n                      dispRefs.current[1] = el;\n                    }}\n                    result=\"dG\"\n                    scale=\"0\"\n                    xChannelSelector=\"R\"\n                    yChannelSelector=\"G\"\n                  />\n                  <feColorMatrix\n                    in=\"dG\"\n                    result=\"cG\"\n                    type=\"matrix\"\n                    values=\"0 0 0 0 0  0 1 0 0 0  0 0 0 0 0  0 0 0 1 0\"\n                  />\n                  <feDisplacementMap\n                    in=\"SourceGraphic\"\n                    in2=\"map\"\n                    ref={(el) => {\n                      dispRefs.current[2] = el;\n                    }}\n                    result=\"dB\"\n                    scale=\"0\"\n                    xChannelSelector=\"R\"\n                    yChannelSelector=\"G\"\n                  />\n                  <feColorMatrix\n                    in=\"dB\"\n                    result=\"cB\"\n                    type=\"matrix\"\n                    values=\"0 0 0 0 0  0 0 0 0 0  0 0 1 0 0  0 0 0 1 0\"\n                  />\n                  <feComposite\n                    in=\"cR\"\n                    in2=\"cG\"\n                    k1=\"0\"\n                    k2=\"1\"\n                    k3=\"1\"\n                    k4=\"0\"\n                    operator=\"arithmetic\"\n                    result=\"cRG\"\n                  />\n                  <feComposite\n                    in=\"cRG\"\n                    in2=\"cB\"\n                    k1=\"0\"\n                    k2=\"1\"\n                    k3=\"1\"\n                    k4=\"0\"\n                    operator=\"arithmetic\"\n                    result=\"refr\"\n                  />\n                </>\n              ) : (\n                <feDisplacementMap\n                  in=\"SourceGraphic\"\n                  in2=\"map\"\n                  ref={(el) => {\n                    dispRefs.current[0] = el;\n                  }}\n                  result=\"refr\"\n                  scale=\"0\"\n                  xChannelSelector=\"R\"\n                  yChannelSelector=\"G\"\n                />\n              )}\n              {specular ? (\n                <>\n                  <feColorMatrix\n                    in=\"map\"\n                    result=\"spec\"\n                    type=\"matrix\"\n                    values=\"0 0 0 0 1  0 0 0 0 1  0 0 0 0 1  0 0 1 0 -0.5019607843\"\n                  />\n                  <feComposite\n                    in=\"spec\"\n                    in2=\"refr\"\n                    k1=\"0\"\n                    k2={SPECULAR_STRENGTH}\n                    k3=\"1\"\n                    k4=\"0\"\n                    operator=\"arithmetic\"\n                  />\n                </>\n              ) : null}\n            </filter>\n          </defs>\n        </svg>\n      ) : null}\n      <div className=\"relative h-full w-full rounded-[inherit]\">{children}</div>\n    </div>\n  );\n}\n\nexport { GlassSurface };\nexport type { GlassSurfaceHandle, GlassSurfaceProps };\n",
      "type": "registry:ui"
    },
    {
      "path": "registry/default/glass/glass-webgl.ts",
      "content": "\"use client\";\n\nimport { generateLensMap, type MapParams } from \"@/components/ui/glass\";\nimport { Children, isValidElement, type ReactNode } from \"react\";\n\nconst SLOTS = 5;\nconst MAP_CACHE_LIMIT = 64;\nconst DPR_CAP = 2.5;\n\ntype RasterKind = \"video\" | \"canvas\" | \"image\";\n\ninterface GlassWebGLParams {\n  depth: number;\n  domeDepth: number;\n  splay: number;\n  specularAngle: number;\n  glow: number;\n  glowSpread: number;\n  glowExponent: number;\n  edgeHighlight: number;\n  edgeWidth: number;\n  edgeExponent: number;\n  mapSize: number;\n  scaleMax: number;\n  maxDisplacement: number;\n  chroma: number;\n  sdfBoundary: boolean;\n  edgeFalloff: boolean;\n  kx: number;\n  ky: number;\n  tint: number;\n  tintRGB: string;\n  specularStrength: number;\n  specularDark: boolean;\n}\n\ninterface GlassWebGLDynamics {\n  zoom?: number;\n  depthMul?: number;\n  mapDims?: { halfW: number; halfH: number; radius: number };\n}\n\ninterface AttachOptions {\n  getParams: () => GlassWebGLParams;\n  getDynamics: () => GlassWebGLDynamics | undefined;\n  reveal: boolean;\n}\n\nconst OVERRIDE_KEYS = [\n  \"depth\",\n  \"domeDepth\",\n  \"splay\",\n  \"specularAngle\",\n  \"glow\",\n  \"glowSpread\",\n  \"glowExponent\",\n  \"edgeHighlight\",\n  \"edgeWidth\",\n  \"edgeExponent\",\n] as const;\n\ntype OverrideKey = (typeof OVERRIDE_KEYS)[number];\n\nfunction clamp01(v: number): number {\n  return v < 0 ? 0 : v > 1 ? 1 : v;\n}\n\nfunction markOverrides(\n  mark: HTMLElement\n): Partial<Record<OverrideKey, number>> & { mul: number } {\n  const out: Partial<Record<OverrideKey, number>> & { mul: number } = {\n    mul: 1,\n  };\n  for (const key of OVERRIDE_KEYS) {\n    const raw = mark.dataset[`glass${key[0].toUpperCase()}${key.slice(1)}`];\n    if (raw !== undefined) {\n      const v = Number(raw);\n      if (!Number.isNaN(v)) {\n        out[key] = v;\n      }\n    }\n  }\n  const mulRaw = mark.dataset.glassMul;\n  if (mulRaw !== undefined) {\n    const v = Number(mulRaw);\n    if (!Number.isNaN(v)) {\n      out.mul = clamp01(v);\n    }\n  }\n  return out;\n}\n\nfunction isRasterChild(children: ReactNode): RasterKind | null {\n  const items = Children.toArray(children).filter(\n    (c) => !(typeof c === \"string\" && c.trim() === \"\")\n  );\n  if (items.length !== 1) {\n    return null;\n  }\n  const only = items[0];\n  if (!isValidElement(only) || typeof only.type !== \"string\") {\n    return null;\n  }\n  if (only.type === \"video\") {\n    return \"video\";\n  }\n  if (only.type === \"canvas\") {\n    return \"canvas\";\n  }\n  if (only.type === \"img\") {\n    return \"image\";\n  }\n  return null;\n}\n\nlet webglProbe: boolean | null = null;\n\nfunction webglAvailable(): boolean {\n  if (webglProbe !== null) {\n    return webglProbe;\n  }\n  if (typeof document === \"undefined\") {\n    webglProbe = false;\n    return false;\n  }\n  try {\n    const canvas = document.createElement(\"canvas\");\n    const gl =\n      canvas.getContext(\"webgl\") ?? canvas.getContext(\"experimental-webgl\");\n    webglProbe = gl instanceof WebGLRenderingContext;\n  } catch {\n    webglProbe = false;\n  }\n  return webglProbe;\n}\n\nconst VERT_SRC = `\nattribute vec2 aPos;\nuniform vec4 uRect;\nuniform vec2 uContainer;\nvarying vec2 vLocal;\nvarying vec2 vGlobalUV;\nvoid main() {\n  vLocal = aPos;\n  vec2 px = uRect.xy + aPos * uRect.zw;\n  vGlobalUV = px / uContainer;\n  vec2 clip = vec2(px.x / uContainer.x * 2.0 - 1.0, 1.0 - px.y / uContainer.y * 2.0);\n  gl_Position = vec4(clip, 0.0, 1.0);\n}\n`;\n\nconst FRAG_SRC = `\nprecision highp float;\nuniform sampler2D uSrc;\nuniform sampler2D uMap;\nuniform vec2 uContainer;\nuniform vec2 uRectWH;\nuniform vec2 uRadius;\nuniform vec3 uChromaScale;\nuniform vec2 uAxis;\nuniform float uSpecStrength;\nuniform float uSpecularDark;\nuniform vec3 uTintColor;\nuniform float uTintAmount;\nvarying vec2 vLocal;\nvarying vec2 vGlobalUV;\n\nfloat rrCoverage(vec2 local, vec2 wh, vec2 rad) {\n  vec2 p = (local - 0.5) * wh;\n  vec2 b = wh * 0.5 - rad;\n  vec2 q = abs(p) - b;\n  float d = length(max(q, 0.0)) + min(max(q.x, q.y), 0.0) - min(rad.x, rad.y);\n  return 1.0 - smoothstep(-1.0, 1.0, d);\n}\n\nvec2 mapOffset(float scalePx) {\n  vec2 m = texture2D(uMap, vLocal).rg;\n  vec2 disp = (m - 0.5) * uAxis * scalePx;\n  return disp / uContainer;\n}\n\nvoid main() {\n  float cov = rrCoverage(vLocal, uRectWH, uRadius);\n  if (cov <= 0.0) {\n    discard;\n  }\n  float r = texture2D(uSrc, vGlobalUV + mapOffset(uChromaScale.r)).r;\n  float g = texture2D(uSrc, vGlobalUV + mapOffset(uChromaScale.g)).g;\n  float b = texture2D(uSrc, vGlobalUV + mapOffset(uChromaScale.b)).b;\n  vec3 col = vec3(r, g, b);\n  float spec = max(texture2D(uMap, vLocal).b - 0.50196078, 0.0);\n  if (uSpecularDark > 0.5) {\n    col *= 1.0 - uSpecStrength * spec;\n  } else {\n    col += uSpecStrength * spec;\n  }\n  col = mix(col, uTintColor, uTintAmount);\n  gl_FragColor = vec4(clamp(col, 0.0, 1.0), cov);\n}\n`;\n\nfunction compileShader(\n  gl: WebGLRenderingContext,\n  type: number,\n  src: string\n): WebGLShader | null {\n  const shader = gl.createShader(type);\n  if (!shader) {\n    return null;\n  }\n  gl.shaderSource(shader, src);\n  gl.compileShader(shader);\n  if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {\n    gl.deleteShader(shader);\n    return null;\n  }\n  return shader;\n}\n\nfunction createProgram(gl: WebGLRenderingContext): WebGLProgram | null {\n  const vs = compileShader(gl, gl.VERTEX_SHADER, VERT_SRC);\n  const fs = compileShader(gl, gl.FRAGMENT_SHADER, FRAG_SRC);\n  if (!vs || !fs) {\n    return null;\n  }\n  const program = gl.createProgram();\n  if (!program) {\n    return null;\n  }\n  gl.attachShader(program, vs);\n  gl.attachShader(program, fs);\n  gl.bindAttribLocation(program, 0, \"aPos\");\n  gl.linkProgram(program);\n  gl.deleteShader(vs);\n  gl.deleteShader(fs);\n  if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {\n    gl.deleteProgram(program);\n    return null;\n  }\n  return program;\n}\n\ntype UniformName =\n  | \"uRect\"\n  | \"uContainer\"\n  | \"uRectWH\"\n  | \"uRadius\"\n  | \"uChromaScale\"\n  | \"uAxis\"\n  | \"uSpecStrength\"\n  | \"uSpecularDark\"\n  | \"uTintColor\"\n  | \"uTintAmount\"\n  | \"uSrc\"\n  | \"uMap\";\n\ninterface MapEntry {\n  texture: WebGLTexture | null;\n  ready: boolean;\n}\n\nfunction parseTintRGB(rgb: string): [number, number, number] {\n  const parts = rgb.split(\",\").map((v) => Number(v.trim()) / 255);\n  return [parts[0] || 0, parts[1] || 0, parts[2] || 0];\n}\n\nclass GlassWebGLRenderer {\n  private gl: WebGLRenderingContext | null = null;\n  private program: WebGLProgram | null = null;\n  private quad: WebGLBuffer | null = null;\n  private srcTexture: WebGLTexture | null = null;\n  private uniforms = new Map<UniformName, WebGLUniformLocation | null>();\n  private mapCache = new Map<string, MapEntry>();\n  private pending = new Set<string>();\n  private raf = 0;\n  private rvfc = 0;\n  private frameReady = false;\n  private srcUploaded = false;\n  private srcW = 0;\n  private srcH = 0;\n  private running = false;\n  private disposed = false;\n\n  attach(\n    container: HTMLDivElement,\n    lensLayer: HTMLDivElement,\n    source: HTMLVideoElement | HTMLCanvasElement | HTMLImageElement,\n    options: AttachOptions\n  ): () => void {\n    const canvas = document.createElement(\"canvas\");\n    canvas.setAttribute(\"aria-hidden\", \"true\");\n    canvas.style.position = \"absolute\";\n    canvas.style.inset = \"0\";\n    canvas.style.width = \"100%\";\n    canvas.style.height = \"100%\";\n    canvas.style.pointerEvents = \"none\";\n    container.appendChild(canvas);\n\n    const gl =\n      canvas.getContext(\"webgl\", {\n        alpha: true,\n        premultipliedAlpha: false,\n        antialias: true,\n        depth: false,\n        stencil: false,\n        preserveDrawingBuffer: false,\n        powerPreference: \"low-power\",\n      }) ?? null;\n\n    if (!(gl instanceof WebGLRenderingContext)) {\n      canvas.remove();\n      return () => undefined;\n    }\n    this.gl = gl;\n\n    const onLost = (event: Event) => {\n      event.preventDefault();\n      this.running = false;\n      cancelAnimationFrame(this.raf);\n      this.srcUploaded = false;\n      this.srcTexture = null;\n      this.srcW = 0;\n      this.srcH = 0;\n      for (const entry of this.mapCache.values()) {\n        entry.texture = null;\n        entry.ready = false;\n      }\n    };\n    const onRestored = () => {\n      if (this.disposed) {\n        return;\n      }\n      this.initGL();\n      this.mapCache.clear();\n      this.pending.clear();\n      this.start();\n    };\n    canvas.addEventListener(\"webglcontextlost\", onLost as EventListener);\n    canvas.addEventListener(\"webglcontextrestored\", onRestored);\n\n    this.initGL();\n\n    const isVideo = source instanceof HTMLVideoElement;\n    const supportsRVFC =\n      isVideo &&\n      \"requestVideoFrameCallback\" in source &&\n      typeof source.requestVideoFrameCallback === \"function\";\n\n    const pumpVideo = () => {\n      if (this.disposed || !(source instanceof HTMLVideoElement)) {\n        return;\n      }\n      this.frameReady = true;\n      this.rvfc = source.requestVideoFrameCallback(pumpVideo);\n    };\n    if (supportsRVFC && source instanceof HTMLVideoElement) {\n      this.rvfc = source.requestVideoFrameCallback(pumpVideo);\n    }\n\n    const tick = () => {\n      if (!this.running) {\n        return;\n      }\n      this.render(\n        container,\n        lensLayer,\n        source,\n        options,\n        isVideo,\n        supportsRVFC\n      );\n      this.raf = requestAnimationFrame(tick);\n    };\n    this.start = () => {\n      if (this.running) {\n        return;\n      }\n      this.running = true;\n      this.raf = requestAnimationFrame(tick);\n    };\n    this.start();\n\n    return () => {\n      this.disposed = true;\n      this.running = false;\n      cancelAnimationFrame(this.raf);\n      if (\n        supportsRVFC &&\n        source instanceof HTMLVideoElement &&\n        \"cancelVideoFrameCallback\" in source\n      ) {\n        source.cancelVideoFrameCallback(this.rvfc);\n      }\n      canvas.removeEventListener(\"webglcontextlost\", onLost as EventListener);\n      canvas.removeEventListener(\"webglcontextrestored\", onRestored);\n      const ctx = this.gl;\n      if (ctx) {\n        for (const entry of this.mapCache.values()) {\n          if (entry.texture) {\n            ctx.deleteTexture(entry.texture);\n          }\n        }\n        if (this.srcTexture) {\n          ctx.deleteTexture(this.srcTexture);\n        }\n        if (this.quad) {\n          ctx.deleteBuffer(this.quad);\n        }\n        if (this.program) {\n          ctx.deleteProgram(this.program);\n        }\n        const lose = ctx.getExtension(\"WEBGL_lose_context\");\n        if (lose) {\n          lose.loseContext();\n        }\n      }\n      this.mapCache.clear();\n      this.pending.clear();\n      this.gl = null;\n      canvas.remove();\n    };\n  }\n\n  private start: () => void = () => undefined;\n\n  private initGL() {\n    const gl = this.gl;\n    if (!gl) {\n      return;\n    }\n    const program = createProgram(gl);\n    if (!program) {\n      return;\n    }\n    this.program = program;\n    const names: UniformName[] = [\n      \"uRect\",\n      \"uContainer\",\n      \"uRectWH\",\n      \"uRadius\",\n      \"uChromaScale\",\n      \"uAxis\",\n      \"uSpecStrength\",\n      \"uSpecularDark\",\n      \"uTintColor\",\n      \"uTintAmount\",\n      \"uSrc\",\n      \"uMap\",\n    ];\n    this.uniforms.clear();\n    for (const name of names) {\n      this.uniforms.set(name, gl.getUniformLocation(program, name));\n    }\n    this.quad = gl.createBuffer();\n    gl.bindBuffer(gl.ARRAY_BUFFER, this.quad);\n    gl.bufferData(\n      gl.ARRAY_BUFFER,\n      new Float32Array([0, 0, 1, 0, 0, 1, 1, 1]),\n      gl.STATIC_DRAW\n    );\n    this.srcTexture = gl.createTexture();\n    gl.bindTexture(gl.TEXTURE_2D, this.srcTexture);\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);\n    gl.enable(gl.BLEND);\n    gl.blendFuncSeparate(\n      gl.SRC_ALPHA,\n      gl.ONE_MINUS_SRC_ALPHA,\n      gl.ONE,\n      gl.ONE_MINUS_SRC_ALPHA\n    );\n    gl.clearColor(0, 0, 0, 0);\n    this.srcUploaded = false;\n  }\n\n  private ensureMap(key: string, mp: MapParams): MapEntry {\n    const existing = this.mapCache.get(key);\n    if (existing) {\n      return existing;\n    }\n    const entry: MapEntry = { texture: null, ready: false };\n    this.mapCache.set(key, entry);\n    if (this.mapCache.size > MAP_CACHE_LIMIT) {\n      const first = this.mapCache.keys().next().value;\n      if (first !== undefined && first !== key) {\n        const old = this.mapCache.get(first);\n        if (old?.texture && this.gl) {\n          this.gl.deleteTexture(old.texture);\n        }\n        this.mapCache.delete(first);\n      }\n    }\n    if (this.pending.has(key)) {\n      return entry;\n    }\n    this.pending.add(key);\n    const url = generateLensMap(mp);\n    if (!url) {\n      this.pending.delete(key);\n      return entry;\n    }\n    const image = new Image();\n    image.onload = () => {\n      this.pending.delete(key);\n      const gl = this.gl;\n      const live = this.mapCache.get(key);\n      if (!gl || this.disposed || !live) {\n        return;\n      }\n      const texture = gl.createTexture();\n      gl.bindTexture(gl.TEXTURE_2D, texture);\n      gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);\n      gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE);\n      gl.texImage2D(\n        gl.TEXTURE_2D,\n        0,\n        gl.RGBA,\n        gl.RGBA,\n        gl.UNSIGNED_BYTE,\n        image\n      );\n      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);\n      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);\n      live.texture = texture;\n      live.ready = true;\n    };\n    image.onerror = () => {\n      this.pending.delete(key);\n    };\n    image.src = url;\n    return entry;\n  }\n\n  private uploadSource(\n    source: HTMLVideoElement | HTMLCanvasElement | HTMLImageElement,\n    isVideo: boolean,\n    supportsRVFC: boolean\n  ): boolean {\n    const gl = this.gl;\n    if (!gl || !this.srcTexture) {\n      return false;\n    }\n    let w = 0;\n    let h = 0;\n    if (source instanceof HTMLVideoElement) {\n      if (source.readyState < source.HAVE_CURRENT_DATA) {\n        return this.srcUploaded;\n      }\n      w = source.videoWidth;\n      h = source.videoHeight;\n    } else if (source instanceof HTMLImageElement) {\n      w = source.naturalWidth;\n      h = source.naturalHeight;\n    } else {\n      w = source.width;\n      h = source.height;\n    }\n    if (w === 0 || h === 0) {\n      return this.srcUploaded;\n    }\n    const needsUpload =\n      !this.srcUploaded ||\n      source instanceof HTMLCanvasElement ||\n      (isVideo && (!supportsRVFC || this.frameReady)) ||\n      (source instanceof HTMLImageElement &&\n        (this.srcW !== w || this.srcH !== h));\n    if (!needsUpload) {\n      return true;\n    }\n    gl.bindTexture(gl.TEXTURE_2D, this.srcTexture);\n    gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);\n    gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE);\n    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source);\n    this.srcUploaded = true;\n    this.srcW = w;\n    this.srcH = h;\n    this.frameReady = false;\n    return true;\n  }\n\n  private render(\n    container: HTMLDivElement,\n    lensLayer: HTMLDivElement,\n    source: HTMLVideoElement | HTMLCanvasElement | HTMLImageElement,\n    options: AttachOptions,\n    isVideo: boolean,\n    supportsRVFC: boolean\n  ) {\n    const gl = this.gl;\n    const program = this.program;\n    if (!gl || !program) {\n      return;\n    }\n    const base = container.getBoundingClientRect();\n    if (base.width < 2 || base.height < 2) {\n      return;\n    }\n    const dpr = Math.min(\n      typeof window === \"undefined\" ? 1 : window.devicePixelRatio || 1,\n      DPR_CAP\n    );\n    const cw = Math.max(1, Math.round(base.width * dpr));\n    const ch = Math.max(1, Math.round(base.height * dpr));\n    if (gl.canvas.width !== cw || gl.canvas.height !== ch) {\n      gl.canvas.width = cw;\n      gl.canvas.height = ch;\n    }\n    gl.viewport(0, 0, cw, ch);\n    gl.clear(gl.COLOR_BUFFER_BIT);\n\n    if (!this.uploadSource(source, isVideo, supportsRVFC)) {\n      return;\n    }\n\n    const p = options.getParams();\n    const dyn = options.getDynamics();\n    const zoom = dyn?.zoom ?? 1;\n    const depthMul = dyn?.depthMul ?? 1;\n    const dispScale = Math.min(p.scaleMax * base.width * zoom, p.maxDisplacement);\n    const tintColor = parseTintRGB(p.tintRGB);\n\n    gl.useProgram(program);\n    gl.bindBuffer(gl.ARRAY_BUFFER, this.quad);\n    gl.enableVertexAttribArray(0);\n    gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);\n    gl.uniform2f(this.uniforms.get(\"uContainer\") ?? null, base.width, base.height);\n    gl.uniform1i(this.uniforms.get(\"uSrc\") ?? null, 0);\n    gl.uniform1i(this.uniforms.get(\"uMap\") ?? null, 1);\n    gl.uniform1f(\n      this.uniforms.get(\"uSpecStrength\") ?? null,\n      p.specularStrength\n    );\n    gl.uniform1f(\n      this.uniforms.get(\"uSpecularDark\") ?? null,\n      p.specularDark ? 1 : 0\n    );\n    gl.uniform3f(\n      this.uniforms.get(\"uTintColor\") ?? null,\n      tintColor[0],\n      tintColor[1],\n      tintColor[2]\n    );\n    gl.activeTexture(gl.TEXTURE0);\n    gl.bindTexture(gl.TEXTURE_2D, this.srcTexture);\n\n    const marks = lensLayer.querySelectorAll<HTMLElement>(\"[data-glass-lens]\");\n    for (let i = 0; i < SLOTS; i++) {\n      const mark = marks[i];\n      if (!mark) {\n        continue;\n      }\n      const r = mark.getBoundingClientRect();\n      if (r.width < 2 || r.height < 2) {\n        continue;\n      }\n      const ov = markOverrides(mark);\n      const tintRaw = mark.dataset.glassTint;\n      const tintNum = tintRaw === undefined ? Number.NaN : Number(tintRaw);\n      const lensTint = Number.isNaN(tintNum) ? p.tint : clamp01(tintNum);\n      const tintEff = 0.5 * lensTint;\n      const md = i === 0 ? dyn?.mapDims : undefined;\n      const rectW = Math.round(r.width * 2) / 2;\n      const rectH = Math.round(r.height * 2) / 2;\n      const genW = md ? Math.round(md.halfW * 4) / 2 : rectW;\n      const genH = md ? Math.round(md.halfH * 4) / 2 : rectH;\n      const radiusParts = getComputedStyle(mark)\n        .borderTopLeftRadius.split(\" \")\n        .map((v) => Math.round(Number.parseFloat(v) * 2) / 2 || 0);\n      const cssRadius = radiusParts[0] ?? 0;\n      const cssRadiusY = radiusParts[1] ?? cssRadius;\n      const radius = md ? Math.round(md.radius * 2) / 2 : cssRadius;\n      const unitsPerPx = md ? genW / Math.max(rectW, 1) : 1;\n      const baseDepth = ov.depth ?? p.depth;\n      const baseDome = ov.domeDepth ?? p.domeDepth;\n      const baseEdgeW = ov.edgeWidth ?? p.edgeWidth;\n      const effDepth = Math.round(baseDepth * depthMul * unitsPerPx * 10) / 10;\n      const effDome = Math.round(baseDome * unitsPerPx * 10) / 10;\n      const effEdgeW = Math.round(baseEdgeW * unitsPerPx * 10) / 10;\n      const splayV = ov.splay ?? p.splay;\n      const angleV = ov.specularAngle ?? p.specularAngle;\n      const glowV = ov.glow ?? p.glow;\n      const glowSpreadV = ov.glowSpread ?? p.glowSpread;\n      const glowExpV = ov.glowExponent ?? p.glowExponent;\n      const edgeV = ov.edgeHighlight ?? p.edgeHighlight;\n      const edgeExpV = ov.edgeExponent ?? p.edgeExponent;\n      const key = `${genW}x${genH}r${radius}d${effDepth}o${effDome}p${splayV}a${angleV}g${glowV},${glowSpreadV},${glowExpV}e${edgeV},${effEdgeW},${edgeExpV}m${p.mapSize}f${p.sdfBoundary ? 1 : 0}${p.edgeFalloff ? 1 : 0}`;\n      const entry = this.ensureMap(key, {\n        halfW: genW / 2,\n        halfH: genH / 2,\n        radius,\n        depth: effDepth,\n        domeDepth: effDome,\n        splay: splayV,\n        specularAngle: angleV,\n        glowStrength: glowV,\n        glowSpread: glowSpreadV,\n        glowExponent: glowExpV,\n        edgeStrength: edgeV,\n        edgeWidth: effEdgeW,\n        edgeExponent: edgeExpV,\n        size: p.mapSize,\n        sdfBoundary: p.sdfBoundary,\n        edgeFalloff: p.edgeFalloff,\n      });\n      if (!entry.ready || !entry.texture) {\n        continue;\n      }\n      const fx = Math.round((r.left - base.left) * 2) / 2;\n      const fy = Math.round((r.top - base.top) * 2) / 2;\n      const tintFade = 1 - 0.85 * tintEff;\n      const axisX = p.kx * ov.mul * tintFade;\n      const axisY = p.ky * ov.mul * tintFade;\n      const c = p.chroma;\n      const chromaR = c > 0 ? dispScale * (1 + 0.2 * c) : dispScale;\n      const chromaG = c > 0 ? dispScale * (1 + 0.1 * c) : dispScale;\n      gl.uniform4f(this.uniforms.get(\"uRect\") ?? null, fx, fy, rectW, rectH);\n      gl.uniform2f(this.uniforms.get(\"uRectWH\") ?? null, rectW, rectH);\n      gl.uniform2f(\n        this.uniforms.get(\"uRadius\") ?? null,\n        Math.min(cssRadius, rectW / 2),\n        Math.min(cssRadiusY, rectH / 2)\n      );\n      gl.uniform3f(\n        this.uniforms.get(\"uChromaScale\") ?? null,\n        chromaR,\n        chromaG,\n        dispScale\n      );\n      gl.uniform2f(this.uniforms.get(\"uAxis\") ?? null, axisX, axisY);\n      gl.uniform1f(\n        this.uniforms.get(\"uTintAmount\") ?? null,\n        Math.round(tintEff * 700) / 1000\n      );\n      gl.activeTexture(gl.TEXTURE1);\n      gl.bindTexture(gl.TEXTURE_2D, entry.texture);\n      gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);\n    }\n  }\n}\n\nexport { GlassWebGLRenderer, isRasterChild, webglAvailable };\nexport type { GlassWebGLDynamics, GlassWebGLParams, RasterKind };\n",
      "type": "registry:ui"
    }
  ],
  "docs": "Wrap content in <Glass> to refract it through a liquid-glass lens. The engine bends only its own children, so put the background inside <Glass> and pass refracting regions through the `lens` prop. Mark each region with `data-glass-lens`; it tracks position, size, and border radius every frame. Tune with scaleX/scaleY (strength), depth (edge band), domeDepth (curvature), chroma, blur, glow, edgeHighlight. For floating UI over arbitrary content the engine cannot reach (portals, the rest of the page), use GlassSurface, also exported from this package. Requires Tailwind v4 and React 19. Gotcha: Tailwind v4 compiles translate-x-*/scale-* to the `translate`/`scale` properties, so transition those, not `transform`, when animating a lens.",
  "type": "registry:ui"
}