{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "prism-glass",
  "title": "Prism Glass",
  "description": "A GPU-accelerated refractive lens for images and rendered textures. Spectral dispersion, uniform frost and a directional rim light, from an SDF-defined shape — no 3D model required. It refracts the texture you give it, not the DOM behind it.",
  "dependencies": [
    "three",
    "@react-three/fiber",
    "@react-three/drei",
    "maath"
  ],
  "files": [
    {
      "path": "packages/prism-glass/src/index.js",
      "content": "/* Both shapes on purpose:\n   - default, so a registry install can be imported as '@/components/prism-glass'\n   - named, which is what the published @morphiq/prism-glass package exposes */\nexport { default } from './PrismGlass.jsx'\nexport { default as PrismGlass } from './PrismGlass.jsx'\n",
      "type": "registry:component",
      "target": "components/prism-glass/index.js"
    },
    {
      "path": "packages/prism-glass/src/PrismGlass.jsx",
      "content": "'use client'\n\nimport { useRef, useMemo, useState, useEffect, Suspense } from 'react'\nimport * as THREE from 'three'\nimport { Canvas, useFrame, useThree } from '@react-three/fiber'\nimport { useTexture } from '@react-three/drei'\nimport { easing } from 'maath'\nimport { vertexShader, fragmentShader } from './shader.js'\nimport { loadSdfTexture } from './sdf.js'\n\nconst SHAPES = { circle: 0, rect: 1, pill: 1, cursor: 2 }\n\n// Stands in for `image` when the caller supplies a `texture` directly, so the\n// loader hook stays unconditional.\nconst BLANK_PX =\n  'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg=='\n\nfunction clamp01(n) {\n  return Math.max(0, Math.min(1, n))\n}\n\n// Normalise the `size` prop: number → circle radius; [w,h] → rect/pill half-extents.\nfunction resolveGeometry({ shape, size, radius }) {\n  if (shape === 'cursor') {\n    const r = (typeof size === 'number' ? size : 20) / 100\n    return { shape: 2, radius: r, half: [r, r], corner: r }\n  }\n  if (shape === 'circle') {\n    const r = (typeof size === 'number' ? size : 20) / 100\n    return { shape: 0, radius: r, half: [r, r], corner: r }\n  }\n  const [w, h] = Array.isArray(size) ? size : [size ?? 28, size ?? 34]\n  const half = [w / 100, h / 100]\n  const corner =\n    shape === 'pill' ? Math.min(half[0], half[1]) : (radius ?? 7) / 100\n  return { shape: 1, radius: Math.min(half[0], half[1]), half, corner }\n}\n\nfunction Surface({ image, mask, texture, optionsRef }) {\n  const matRef = useRef()\n  const loaded = useTexture(image)\n  const tex = texture || loaded\n  const { viewport, size } = useThree()\n\n  useMemo(() => {\n    tex.wrapS = tex.wrapT = THREE.ClampToEdgeWrapping\n    tex.minFilter = THREE.LinearMipmapLinearFilter\n    tex.generateMipmaps = true\n    tex.needsUpdate = true\n  }, [tex])\n\n  const uniforms = useMemo(\n    () => ({\n      uTex: { value: null },\n      uAspect: { value: 1 },\n      uImgAspect: { value: 1 },\n      uCenter: { value: new THREE.Vector2(0.5, 0.5) },\n      uSize: { value: new THREE.Vector2(0.28, 0.34) },\n      uRadius: { value: 0.2 },\n      uCorner: { value: 0.07 },\n      uShape: { value: 0 },\n      uPx: { value: 0.001 },\n      uMask: { value: null },\n      uUseMask: { value: 0 },\n      uMaskAspect: { value: 1 },\n      uLightAngle: { value: 0 },\n      uLightInt: { value: 1 },\n      uRefraction: { value: 1 },\n      uDepth: { value: 0.6 },\n      uDispersion: { value: 0 },\n      uFrost: { value: 0 },\n      uSplay: { value: 0 },\n      uAlpha: { value: 0 },\n    }),\n    []\n  )\n\n  const [sdf, setSdf] = useState(null)\n  useEffect(() => {\n    if (!mask) { setSdf(null); return }\n    let alive = true\n    loadSdfTexture(mask)\n      .then((r) => { if (alive) setSdf(r) })\n      .catch(() => { if (alive) setSdf(null) })\n    return () => { alive = false }\n  }, [mask])\n\n  useFrame((state, delta) => {\n    const u = matRef.current.uniforms\n    const o = optionsRef.current\n    const g = resolveGeometry(o)\n\n    u.uTex.value = tex\n    u.uAlpha.value = o.transparent ? 1 : 0\n    u.uAspect.value = size.width / size.height\n    u.uPx.value = 1 / Math.max(1, state.gl.domElement.height)\n    u.uImgAspect.value = tex.image ? tex.image.width / tex.image.height : 1\n\n    u.uShape.value = g.shape\n    u.uUseMask.value = sdf ? 1 : 0\n    u.uMask.value = sdf ? sdf.texture : null\n    u.uMaskAspect.value = sdf ? sdf.aspect : 1\n    u.uRadius.value = g.radius\n    u.uCorner.value = g.corner\n    u.uSize.value.set(g.half[0], g.half[1])\n\n    u.uLightAngle.value = (o.lightAngle * Math.PI) / 180\n    u.uLightInt.value = o.lightIntensity / 100\n    u.uRefraction.value = o.refraction / 100\n    u.uDepth.value = o.depth / 100\n    u.uDispersion.value = o.dispersion / 100\n    u.uFrost.value = o.frost / 100\n    u.uSplay.value = o.splay / 100\n\n    const target =\n      o.mode === 'cursor'\n        ? [state.pointer.x * 0.5 + 0.5, state.pointer.y * 0.5 + 0.5]\n        : [clamp01(o.position[0]), clamp01(o.position[1])]\n\n    if (o.follow > 0) {\n      easing.damp2(u.uCenter.value, target, o.follow, delta)\n    } else {\n      u.uCenter.value.set(target[0], target[1])\n    }\n  })\n\n  return (\n    <mesh scale={[viewport.width, viewport.height, 1]}>\n      <planeGeometry />\n      <shaderMaterial\n        ref={matRef}\n        uniforms={uniforms}\n        vertexShader={vertexShader}\n        fragmentShader={fragmentShader}\n        transparent\n        depthWrite={false}\n        premultipliedAlpha\n      />\n    </mesh>\n  )\n}\n\n/**\n * PrismGlass — a programmable liquid-glass refraction surface.\n * Renders `image` full-bleed and bends it through a glass lens.\n */\nexport default function PrismGlass({\n  image = BLANK_PX,\n  mask,\n  texture,\n  transparent = false,\n  shape = 'circle',\n  mode = 'cursor',\n  size = 20,\n  radius = 7,\n  position = [0.5, 0.5],\n  follow = 0.09,\n  refraction = 100,\n  depth = 60,\n  dispersion = 0,\n  frost = 0,\n  splay = 0,\n  lightAngle = 45,\n  lightIntensity = 100,\n  dpr = [1, 2],\n  className,\n  style,\n}) {\n  const optionsRef = useRef({})\n  optionsRef.current = {\n    shape, mode, size, radius, position, follow,\n    refraction, depth, dispersion, frost, splay,\n    lightAngle, lightIntensity, transparent,\n  }\n\n  return (\n    <div\n      className={className}\n      style={{\n        position: 'relative',\n        width: '100%',\n        height: '100%',\n        overflow: 'hidden',\n        ...style,\n      }}\n    >\n      <Canvas\n        gl={{ antialias: true, alpha: true, premultipliedAlpha: true }}\n        onCreated={({ gl }) => gl.setClearAlpha(0)}\n        dpr={dpr}\n        style={{ display: 'block', width: '100%', height: '100%' }}\n      >\n        <Suspense fallback={null}>\n          <Surface image={image} mask={mask} texture={texture} optionsRef={optionsRef} />\n        </Suspense>\n      </Canvas>\n    </div>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/prism-glass/PrismGlass.jsx"
    },
    {
      "path": "packages/prism-glass/src/shader.js",
      "content": "export const vertexShader = /* glsl */ `\n  varying vec2 vUv;\n  void main() {\n    vUv = uv;\n    gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n  }\n`\n\nexport const fragmentShader = /* glsl */ `\n  precision highp float;\n  uniform sampler2D uTex;\n  uniform float uAspect;     // view aspect (w/h)\n  uniform float uImgAspect;  // image aspect (w/h)\n  uniform vec2  uCenter;\n  uniform vec2  uSize;\n  uniform float uRadius;\n  uniform float uCorner;\n  uniform float uShape;\n  uniform float uPx;\n  uniform sampler2D uMask;\n  uniform float uUseMask;\n  uniform float uMaskAspect;\n  uniform float uLightAngle;\n  uniform float uLightInt;\n  uniform float uRefraction;\n  uniform float uDepth;\n  uniform float uDispersion;\n  uniform float uFrost;\n  uniform float uSplay;\n  uniform float uAlpha;      // 1 = draw only the lens, transparent elsewhere\n  varying vec2 vUv;\n\n  // signed distance to a rounded rectangle (<0 inside)\n  float sdRound(vec2 p, vec2 b, float r) {\n    vec2 q = abs(p) - b + r;\n    return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - r;\n  }\n\n  // map a screen uv to a \"cover\" sample of the image (no stretch)\n  vec2 coverUv(vec2 uv) {\n    vec2 s = (uAspect > uImgAspect)\n      ? vec2(1.0, uImgAspect / uAspect)\n      : vec2(uAspect / uImgAspect, 1.0);\n    return (uv - 0.5) * s + 0.5;\n  }\n\n  vec3 sampleBG(vec2 uv, float lod) {\n    return texture2D(uTex, clamp(coverUv(uv), 0.001, 0.999), lod).rgb;\n  }\n\n  // Visible-spectrum response for t in [0,1] (red → violet) built from broad,\n  // heavily OVERLAPPING Gaussian lobes. Overlap is the point: no value of t\n  // ever yields a pure primary, so neighbouring wavelengths blend into a\n  // continuous iridescence instead of banding into discrete colour channels.\n  vec3 spectrum(float t) {\n    float r = exp(-pow((t - 0.16) / 0.42, 2.0));\n    float g = exp(-pow((t - 0.50) / 0.42, 2.0));\n    float b = exp(-pow((t - 0.84) / 0.42, 2.0));\n    return vec3(r, g, b);\n  }\n\n  /* ---- cursor arrow: exact polygon SDF from the source SVG ----\n     Vertices are the SVG path, centred and normalised so the arrow's height\n     is 1.0, with Y flipped (SVG is y-down). Unrolled for 4 edges so there is\n     no dynamic array indexing — compiles everywhere. */\n  const vec2 A0 = vec2(-0.412371,  0.500000);\n  const vec2 A1 = vec2( 0.411402, -0.065866);\n  const vec2 A2 = vec2(-0.085669, -0.065866);\n  const vec2 A3 = vec2(-0.334205, -0.496340);\n\n  float segD2(vec2 p, vec2 a, vec2 b) {\n    vec2 e = b - a;\n    vec2 w = p - a;\n    vec2 q = w - e * clamp(dot(w, e) / dot(e, e), 0.0, 1.0);\n    return dot(q, q);\n  }\n\n  // even-odd crossing test, division-free (handles horizontal edges safely)\n  bool flipSide(vec2 p, vec2 a, vec2 b) {\n    vec2 e = b - a;\n    vec2 w = p - a;\n    bvec3 c = bvec3(p.y >= a.y, p.y < b.y, e.x * w.y > e.y * w.x);\n    return all(c) || all(not(c));\n  }\n\n  float sdArrow(vec2 p) {\n    float d = min(min(segD2(p, A0, A1), segD2(p, A1, A2)),\n                  min(segD2(p, A2, A3), segD2(p, A3, A0)));\n    float s = 1.0;\n    if (flipSide(p, A0, A1)) s = -s;\n    if (flipSide(p, A1, A2)) s = -s;\n    if (flipSide(p, A2, A3)) s = -s;\n    if (flipSide(p, A3, A0)) s = -s;\n    return s * sqrt(d);\n  }\n\n  float sdf(vec2 p) {\n    // A mask overrides the built-in shapes: sample a signed-distance texture so\n    // any silhouette (curves included) behaves like an analytic SDF.\n    if (uUseMask > 0.5) {\n      float fullH = 2.0 * uRadius;\n      vec2  half_ = vec2(fullH * uMaskAspect, fullH) * 0.5;\n      vec2  muv   = p / (2.0 * half_) + 0.5;\n      if (muv.x < 0.0 || muv.x > 1.0 || muv.y < 0.0 || muv.y > 1.0) return fullH;\n      return (texture2D(uMask, muv).r - 0.5) * fullH;\n    }\n    if (uShape > 1.5) {\n      float k = 2.0 * uRadius;              // arrow height spans 2*uRadius\n      return sdArrow(p / k) * k;\n    }\n    return (uShape < 0.5) ? length(p) - uRadius : sdRound(p, uSize, uCorner);\n  }\n\n  void main() {\n    // aspect-corrected space so corners stay round\n    vec2 q  = vUv - uCenter;\n    vec2 qa = vec2(q.x * uAspect, q.y);\n    // uShape: 0 = circle (uRadius), 1 = rounded rect / pill (uSize + uCorner)\n    float d = sdf(qa);\n\n    // Antialias the silhouette. d is a true distance, so one pixel of it is\n    // uPx. Shade the transition band too and blend by coverage below — a hard\n    // d > 0.0 cutoff is what makes the outline look pixelated.\n    float aa = max(uPx * 1.5, 1e-6);\n    if (d > aa) {\n      // Outside the lens: either the plain backdrop, or nothing at all when\n      // the surface is meant to sit over live page content.\n      gl_FragColor = (uAlpha > 0.5)\n        ? vec4(0.0)\n        : vec4(sampleBG(vUv, 0.0), 1.0);\n      return;\n    }\n\n    // DEPTH = reach: how far the refraction band extends in from the border.\n    //   low depth → thin edge band; high depth → wider band but the CENTER\n    //   still stays clear (matches the reference even at depth 100).\n    float mn    = (uShape > 0.5 && uShape < 1.5) ? min(uSize.x, uSize.y) : uRadius;\n    float bw    = mix(mn * 0.04, mn * 0.52, uDepth);\n    float edge  = -d;                         // 0 at border → grows inward\n    float t     = clamp(edge / bw, 0.0, 1.0); // 0 border, 1 inner (flat center)\n    float slope = pow(1.0 - t, 1.8);          // strong at rim, 0 in the centre\n\n    // outward normal = SDF gradient, WIDE stencil so it rotates smoothly around\n    // the corners instead of flipping along the diagonals (kills the \"X\" seam).\n    // Outward normal via a wide-stencil gradient. The wide stencil is what\n    // makes the direction rotate smoothly around rounded-rect corners instead\n    // of flipping along the diagonals (which would show as an \"X\" seam).\n    float eps = 0.03;\n    vec2 grad = vec2(\n      sdf(qa + vec2(eps, 0.0)) - sdf(qa - vec2(eps, 0.0)),\n      sdf(qa + vec2(0.0, eps)) - sdf(qa - vec2(0.0, eps))\n    );\n    grad = normalize(grad + 1e-6);\n\n    // normal & tangent, in UV space (aspect-aware)\n    vec2 nrm = normalize(vec2(grad.x / uAspect, grad.y));\n    vec2 tng = vec2(-nrm.y, nrm.x);\n    float qt = dot(q, tng);   // tangential position from the panel centre\n\n    // REFRACTION is the MASTER strength — it gates the entire effect. At 0 the\n    // glass is invisible no matter what Depth/Splay/Dispersion are.\n    //   • base NORMAL refraction bends the content in the band (subtle).\n    //   • SPLAY adds TANGENTIAL spread (stretch along the edge).\n    // DEPTH scales the warp MAGNITUDE (quadratic → gentle mid-range, bold and\n    // liquid-metal at 100), not just the reach.\n    vec2 dispNormal     = -nrm * slope * (0.02 + uDepth * uDepth * 0.26);\n    vec2 dispTangential = -tng * qt * slope * uSplay * 0.85;\n    vec2 disp = (dispNormal + dispTangential) * uRefraction;\n\n    // DISPERSION scales with the LOCAL WARP magnitude, so bold colour bands\n    // spread through the whole distorted zone (not a thin line on the rim).\n    // Quadratic response: ~invisible at 25, bold and saturated at 100.\n    float dsp = uDispersion * uDispersion * uRefraction;\n    float ds  = dsp * (length(disp) * 0.38 + slope * 0.008);\n    vec2  dd  = nrm * ds;\n\n    // frost = clean uniform mip blur; a touch of blur scaled to the warp size\n    // keeps big displacements reading as smooth liquid glass (not fine ripples).\n    // extra blur where dispersion is active merges the colour split into broad\n    // smooth iridescent bands instead of fine multicoloured noise.\n    float lod = slope * 0.6 + length(disp) * 5.0 + dsp * 1.3;\n\n    // SPECTRAL dispersion: integrate many samples across the visible spectrum,\n    // each displaced by a wavelength-dependent amount and weighted by its\n    // spectral colour. This yields a continuous chromatic texture (real prism\n    // behaviour) rather than three separated R/G/B ghost layers.\n    vec3 col  = vec3(0.0);\n    vec3 wsum = vec3(0.0);\n    for (int i = 0; i < 28; i++) {\n      float t = float(i) / 27.0;\n      vec3  w = spectrum(t);\n      vec2  off = dd * (t - 0.5) * 2.0;\n      col  += sampleBG(vUv + disp + off, lod) * w;\n      wsum += w;\n    }\n    col /= max(wsum, vec3(1e-4));\n\n    // FROST: a uniform diffusing layer across the WHOLE glass — a constant\n    // radius disc blur (golden-angle spiral for even, streak-free coverage).\n    // Deliberately independent of slope/warp so the frosting is even everywhere,\n    // the way a real etched/frosted layer on the lens behaves.\n    if (uFrost > 0.001) {\n      float r    = uFrost * 0.055;\n      float flod = lod + uFrost * 4.5;   // mip blur carries most of the diffusion\n                                         // so the 16 taps never read as ghosts\n      vec3  acc  = vec3(0.0);\n      for (int i = 0; i < 16; i++) {\n        float fi  = float(i);\n        float a   = fi * 2.39996323;                  // golden angle\n        float rad = r * sqrt((fi + 0.5) / 16.0);      // uniform disc density\n        vec2  o   = vec2(cos(a) / uAspect, sin(a)) * rad;\n        acc += sampleBG(vUv + disp + o, flod);\n      }\n      col = mix(col, acc / 16.0, clamp(uFrost * 1.35, 0.0, 1.0));\n    }\n\n    // LIGHT: a bright rim STROKE at the very border, INDEPENDENT of refraction\n    // (it shows even with refraction 0). Intensity = brightness; Angle = which\n    // side of the rim catches the light, with a faint base glow all around.\n    float rimW     = mn * 0.06;                          // stroke width from border\n    float rim      = 1.0 - smoothstep(0.0, rimW, edge);  // 1 at border → 0 inward\n    vec2  Ldir     = vec2(cos(uLightAngle), sin(uLightAngle));\n    float facing   = max(dot(nrm, Ldir), 0.0);\n    float topBias  = max(nrm.y, 0.0);   // the top of the rim always catches a bit more\n    float rimLight = rim * uLightInt * (0.16 + 0.6 * facing + 0.28 * topBias);\n    col += rimLight;\n\n    // coverage: 1 well inside, 0 just outside, smooth across one pixel\n    float cov = 1.0 - smoothstep(-aa, aa, d);\n\n    if (uAlpha > 0.5) {\n      // Premultiplied, so the lens composites cleanly over whatever is behind\n      // it in the DOM instead of carrying its own copy of the backdrop.\n      gl_FragColor = vec4(col * cov, cov);\n      return;\n    }\n\n    col = mix(sampleBG(vUv, 0.0), col, cov);\n    gl_FragColor = vec4(col, 1.0);\n  }\n`\n\n",
      "type": "registry:component",
      "target": "components/prism-glass/shader.js"
    },
    {
      "path": "packages/prism-glass/src/sdf.js",
      "content": "import * as THREE from 'three'\n\n/* Turn any silhouette (SVG / PNG) into a signed-distance-field texture, so the\n   shader can treat an arbitrary shape exactly like an analytic SDF.\n   Encoding: r = 0.5 on the outline, <0.5 inside, >0.5 outside, in units of the\n   padded image height. Bilinear filtering of an SDF keeps edges sharp. */\n\nconst SQRT2 = Math.SQRT2\n\n// two-pass chamfer distance transform; `seed[i] === 1` means distance 0\nfunction chamfer(seed, W, H) {\n  const d = new Float32Array(W * H)\n  d.fill(1e9)\n  for (let i = 0; i < W * H; i++) if (seed[i]) d[i] = 0\n\n  for (let y = 0; y < H; y++) {\n    for (let x = 0; x < W; x++) {\n      const i = y * W + x\n      let v = d[i]\n      if (x > 0) v = Math.min(v, d[i - 1] + 1)\n      if (y > 0) v = Math.min(v, d[i - W] + 1)\n      if (x > 0 && y > 0) v = Math.min(v, d[i - W - 1] + SQRT2)\n      if (x < W - 1 && y > 0) v = Math.min(v, d[i - W + 1] + SQRT2)\n      d[i] = v\n    }\n  }\n  for (let y = H - 1; y >= 0; y--) {\n    for (let x = W - 1; x >= 0; x--) {\n      const i = y * W + x\n      let v = d[i]\n      if (x < W - 1) v = Math.min(v, d[i + 1] + 1)\n      if (y < H - 1) v = Math.min(v, d[i + W] + 1)\n      if (x < W - 1 && y < H - 1) v = Math.min(v, d[i + W + 1] + SQRT2)\n      if (x > 0 && y < H - 1) v = Math.min(v, d[i + W - 1] + SQRT2)\n      d[i] = v\n    }\n  }\n  return d\n}\n\nfunction build(img, res) {\n  // rasterise at `res` tall for precision, with padding so outside distances grow\n  const ar = img.width / img.height\n  const iw = Math.max(1, Math.round(res * ar))\n  const ih = res\n  const pad = Math.round(Math.max(iw, ih) * 0.35)\n  const W = iw + pad * 2\n  const H = ih + pad * 2\n\n  const c = document.createElement('canvas')\n  c.width = W\n  c.height = H\n  const ctx = c.getContext('2d', { willReadFrequently: true })\n  ctx.clearRect(0, 0, W, H)\n  ctx.drawImage(img, pad, pad, iw, ih)\n  const px = ctx.getImageData(0, 0, W, H).data\n\n  // opaque = inside; if the art has no alpha, fall back to \"not white\"\n  let maxA = 0\n  for (let i = 3; i < px.length; i += 4) if (px[i] > maxA) maxA = px[i]\n  const useAlpha = maxA > 8\n\n  const inside = new Uint8Array(W * H)\n  const outside = new Uint8Array(W * H)\n  for (let i = 0; i < W * H; i++) {\n    const o = i * 4\n    const on = useAlpha\n      ? px[o + 3] > 127\n      : px[o + 3] > 127 && (px[o] + px[o + 1] + px[o + 2]) / 3 < 240\n    inside[i] = on ? 1 : 0\n    outside[i] = on ? 0 : 1\n  }\n\n  const dOut = chamfer(inside, W, H)  // outside → nearest shape pixel\n  const dIn = chamfer(outside, W, H)  // inside  → nearest background pixel\n\n  // encode, flipping vertically (canvas is y-down, GL textures are y-up)\n  const data = new Uint8Array(W * H)\n  for (let y = 0; y < H; y++) {\n    const src = (H - 1 - y) * W\n    const dst = y * W\n    for (let x = 0; x < W; x++) {\n      const i = src + x\n      const signed = inside[i] ? -dIn[i] : dOut[i]\n      const enc = 0.5 + signed / H\n      data[dst + x] = Math.max(0, Math.min(255, Math.round(enc * 255)))\n    }\n  }\n\n  const texture = new THREE.DataTexture(data, W, H, THREE.RedFormat)\n  texture.minFilter = THREE.LinearFilter\n  texture.magFilter = THREE.LinearFilter\n  texture.wrapS = texture.wrapT = THREE.ClampToEdgeWrapping\n  texture.needsUpdate = true\n  return { texture, aspect: W / H }\n}\n\nexport function loadSdfTexture(url, res = 256) {\n  return new Promise((resolve, reject) => {\n    const img = new Image()\n    img.crossOrigin = 'anonymous'\n    img.onload = () => {\n      try { resolve(build(img, res)) } catch (e) { reject(e) }\n    }\n    img.onerror = reject\n    img.src = url\n  })\n}\n",
      "type": "registry:component",
      "target": "components/prism-glass/sdf.js"
    },
    {
      "path": "packages/prism-glass/types/index.d.ts",
      "content": "import type { CSSProperties } from 'react'\n\nexport interface PrismGlassProps {\n  /** Image URL rendered full-bleed behind (and refracted by) the glass. */\n  image: string\n  /** Silhouette (SVG or PNG) used as the lens outline. Overrides `shape`.\n   *  Converted to a signed-distance field on load, so curves work. */\n  mask?: string\n  /** Lens outline. `pill` is a rounded rect with fully-rounded ends. */\n  shape?: 'circle' | 'rect' | 'pill' | 'cursor'\n  /** `cursor` follows the pointer; `static` sits at `position`. */\n  mode?: 'cursor' | 'static'\n  /** circle/cursor: radius i.e. half-height (0-100). rect/pill: `[width, height]`. */\n  size?: number | [number, number]\n  /** Corner radius (0-100) for `shape=\"rect\"`. Ignored otherwise. */\n  radius?: number\n  /** Position in `static` mode, normalised `[x, y]` from 0 to 1. */\n  position?: [number, number]\n  /** Follow easing. 0 snaps instantly; higher trails more. */\n  follow?: number\n  /** Master strength (0-100). At 0 the glass is invisible. */\n  refraction?: number\n  /** Reach of the refraction band and the warp magnitude (0-100). */\n  depth?: number\n  /** Spectral colour separation (0-100). */\n  dispersion?: number\n  /** Uniform frosting across the lens (0-100). */\n  frost?: number\n  /** Tangential stretch along the edge (0-100). */\n  splay?: number\n  /** Direction of the rim light, in degrees. */\n  lightAngle?: number\n  /** Rim light brightness (0-100). */\n  lightIntensity?: number\n  /** Device pixel ratio passed to the renderer. */\n  dpr?: number | [number, number]\n  className?: string\n  style?: CSSProperties\n}\n\nexport declare function PrismGlass(props: PrismGlassProps): JSX.Element\n",
      "type": "registry:file",
      "target": "components/prism-glass/index.d.ts"
    }
  ],
  "type": "registry:component"
}