{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "magnetic-beams-background",
  "type": "registry:ui",
  "title": "MagneticBeamsBackground",
  "description": "Animated landing-page background: the GridBeams line grid with seeded light beams that are attracted toward the pointer while it hovers, then resume their traversal from that point when it leaves. Motion-powered, SSR-deterministic, offscreen-paused, with a designed reduced-motion static fallback.",
  "dependencies": [
    "motion@^12.43.0"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://components.dethink.co.uk/r/dethink-base.json"
  ],
  "files": [
    {
      "path": "packages/components/src/components/magnetic-beams-background/magnetic-beams-background.tsx",
      "type": "registry:file",
      "target": "~/components/dethink/components/magnetic-beams-background/magnetic-beams-background.tsx",
      "content": "import {\n  forwardRef,\n  useCallback,\n  useEffect,\n  useLayoutEffect,\n  useMemo,\n  useRef,\n  useState,\n  type CSSProperties,\n  type ForwardedRef,\n  type HTMLAttributes,\n  type PointerEvent as ReactPointerEvent,\n  type ReactNode,\n} from \"react\";\nimport {\n  MotionConfig,\n  animate,\n  motion as motionElement,\n  useMotionValue,\n  useReducedMotion,\n  useTransform,\n  type AnimationPlaybackControls,\n  type MotionValue,\n  type Transition,\n} from \"motion/react\";\nimport { cn } from \"../../utils/cn\";\nimport { usePageVisible } from \"../../utils/use-page-visible\";\nimport { mulberry32, seededRange } from \"../../utils/seeded-random\";\n\nexport type MagneticBeamsBackgroundDensity = \"sparse\" | \"normal\" | \"dense\";\nexport type MagneticBeamsBackgroundMode = \"magnetic\" | \"follow\";\nexport type MagneticBeamsBackgroundIntensity = \"faint\" | \"subtle\" | \"bold\";\nexport type MagneticBeamsBackgroundSpeed = \"slow\" | \"normal\" | \"fast\";\nexport type MagneticBeamsBackgroundTone = \"foreground\" | \"muted\" | \"primary\";\nexport type MagneticBeamsBackgroundBeamAxis = \"x\" | \"y\";\n\ntype MotionBackedEventProps =\n  | \"onAnimationEnd\"\n  | \"onAnimationEndCapture\"\n  | \"onAnimationIteration\"\n  | \"onAnimationIterationCapture\"\n  | \"onAnimationStart\"\n  | \"onAnimationStartCapture\"\n  | \"onDrag\"\n  | \"onDragCapture\"\n  | \"onDragEnd\"\n  | \"onDragEndCapture\"\n  | \"onDragEnter\"\n  | \"onDragEnterCapture\"\n  | \"onDragExit\"\n  | \"onDragExitCapture\"\n  | \"onDragLeave\"\n  | \"onDragLeaveCapture\"\n  | \"onDragOver\"\n  | \"onDragOverCapture\"\n  | \"onDragStart\"\n  | \"onDragStartCapture\";\n\ntype MotionSafeDivProps = Omit<\n  HTMLAttributes<HTMLDivElement>,\n  MotionBackedEventProps\n>;\n\nexport interface MagneticBeamsBackgroundProps extends MotionSafeDivProps {\n  animate?: boolean;\n  children?: ReactNode;\n  density?: MagneticBeamsBackgroundDensity;\n  intensity?: MagneticBeamsBackgroundIntensity;\n  interactive?: boolean;\n  mode?: MagneticBeamsBackgroundMode;\n  seed?: number;\n  speed?: MagneticBeamsBackgroundSpeed;\n  tone?: MagneticBeamsBackgroundTone;\n}\n\nexport interface MagneticBeamsBackgroundMotionConfig {\n  animateEnabled: boolean;\n  attractTransition: Transition;\n  beamDuration: number;\n  reducedMotion: boolean;\n  transition: Transition;\n}\n\nexport interface MagneticBeamsBackgroundBeam {\n  axis: MagneticBeamsBackgroundBeamAxis;\n  delay: number;\n  position: number;\n  repeatDelay: number;\n  staticOffset: number;\n}\n\nconst magneticBeamsBackgroundSpeedDurations: Record<\n  MagneticBeamsBackgroundSpeed,\n  number\n> = {\n  slow: 9,\n  normal: 6,\n  fast: 3.5,\n};\n\nexport function getMagneticBeamsBackgroundMotionConfig(\n  speed: MagneticBeamsBackgroundSpeed,\n  reducedMotion = false,\n): MagneticBeamsBackgroundMotionConfig {\n  if (reducedMotion) {\n    return {\n      animateEnabled: false,\n      attractTransition: { duration: 0 },\n      beamDuration: 0,\n      reducedMotion: true,\n      transition: { duration: 0 },\n    };\n  }\n\n  const beamDuration = magneticBeamsBackgroundSpeedDurations[speed];\n\n  return {\n    animateEnabled: true,\n    attractTransition: { type: \"spring\", stiffness: 120, damping: 24 },\n    beamDuration,\n    reducedMotion: false,\n    transition: { duration: beamDuration, ease: \"linear\", repeat: Infinity },\n  };\n}\n\nconst magneticBeamsBackgroundBeamCounts: Record<\n  MagneticBeamsBackgroundDensity,\n  number\n> = {\n  sparse: 3,\n  normal: 5,\n  dense: 7,\n};\n\nfunction roundTo(value: number, decimals: number): number {\n  const factor = 10 ** decimals;\n  return Math.round(value * factor) / factor;\n}\n\nexport function getMagneticBeamsBackgroundGeometry(\n  seed: number,\n  density: MagneticBeamsBackgroundDensity,\n): MagneticBeamsBackgroundBeam[] {\n  const random = mulberry32(seed);\n\n  return Array.from(\n    { length: magneticBeamsBackgroundBeamCounts[density] },\n    () => ({\n      axis: (random() > 0.5 ? \"x\" : \"y\") as MagneticBeamsBackgroundBeamAxis,\n      position: Math.round(seededRange(random, 8, 92)),\n      delay: roundTo(seededRange(random, 0, 4), 2),\n      repeatDelay: roundTo(seededRange(random, 1.5, 5), 2),\n      staticOffset: Math.round(seededRange(random, 10, 60)),\n    }),\n  );\n}\n\n// A beam spans 30% of its rail and travels from -100% to 433% of its own\n// length, so a full pass carries it fully offscreen on both ends. Progress is\n// the normalized 0..1 position within that pass.\nconst magneticBeamsBackgroundBeamLengthFraction = 0.3;\nconst magneticBeamsBackgroundTravelStart = -100;\nconst magneticBeamsBackgroundTravelSpan = 533;\n\nexport function getMagneticBeamsBackgroundPointerProgress(\n  pointerFraction: number,\n): number {\n  const fraction = Math.min(1, Math.max(0, pointerFraction));\n  const translate =\n    ((fraction - magneticBeamsBackgroundBeamLengthFraction / 2) * 100) /\n    magneticBeamsBackgroundBeamLengthFraction;\n  const progress =\n    (translate - magneticBeamsBackgroundTravelStart) /\n    magneticBeamsBackgroundTravelSpan;\n\n  return Math.min(1, Math.max(0, progress));\n}\n\n// In \"follow\" mode a beam covers the distance to the pointer at its normal\n// pass speed instead of a spring, so the chase reads as steady travel.\nexport function getMagneticBeamsBackgroundFollowDuration(\n  currentProgress: number,\n  targetProgress: number,\n  beamDuration: number,\n): number {\n  return Math.abs(targetProgress - currentProgress) * beamDuration;\n}\n\nconst magneticBeamsBackgroundRootClasses = \"relative isolate overflow-hidden\";\n\nconst magneticBeamsBackgroundLayerClasses =\n  \"pointer-events-none absolute inset-0 select-none\";\n\nconst magneticBeamsBackgroundContentClasses = \"relative z-10\";\n\nconst magneticBeamsBackgroundPatternClasses =\n  \"absolute inset-0 [background-image:linear-gradient(to_right,currentColor_1px,transparent_1px),linear-gradient(to_bottom,currentColor_1px,transparent_1px)]\";\n\nconst magneticBeamsBackgroundDensityClasses: Record<\n  MagneticBeamsBackgroundDensity,\n  string\n> = {\n  sparse: \"[background-size:96px_96px]\",\n  normal: \"[background-size:56px_56px]\",\n  dense: \"[background-size:32px_32px]\",\n};\n\nconst magneticBeamsBackgroundToneIntensityClasses: Record<\n  MagneticBeamsBackgroundTone,\n  Record<MagneticBeamsBackgroundIntensity, string>\n> = {\n  foreground: {\n    faint: \"text-foreground/5 dark:text-foreground/10\",\n    subtle: \"text-foreground/10 dark:text-foreground/15\",\n    bold: \"text-foreground/15 dark:text-foreground/25\",\n  },\n  muted: {\n    faint: \"text-muted-foreground/10 dark:text-muted-foreground/15\",\n    subtle: \"text-muted-foreground/15 dark:text-muted-foreground/25\",\n    bold: \"text-muted-foreground/25 dark:text-muted-foreground/40\",\n  },\n  primary: {\n    faint: \"text-primary/5 dark:text-primary/10\",\n    subtle: \"text-primary/10 dark:text-primary/20\",\n    bold: \"text-primary/20 dark:text-primary/30\",\n  },\n};\n\nconst magneticBeamsBackgroundBeamToneClasses: Record<\n  MagneticBeamsBackgroundTone,\n  string\n> = {\n  foreground: \"text-foreground/40 dark:text-foreground/50\",\n  muted: \"text-muted-foreground/40 dark:text-muted-foreground/50\",\n  primary: \"text-primary/60 dark:text-primary/70\",\n};\n\nconst magneticBeamsBackgroundRailClasses: Record<\n  MagneticBeamsBackgroundBeamAxis,\n  string\n> = {\n  x: \"absolute inset-x-0 h-px\",\n  y: \"absolute inset-y-0 w-px\",\n};\n\nconst magneticBeamsBackgroundBeamClasses: Record<\n  MagneticBeamsBackgroundBeamAxis,\n  string\n> = {\n  x: \"absolute inset-y-0 w-[30%] bg-gradient-to-r from-transparent via-current to-transparent\",\n  y: \"absolute inset-x-0 h-[30%] bg-gradient-to-b from-transparent via-current to-transparent\",\n};\n\nexport interface MagneticBeamsBackgroundClassNamesOptions {\n  className?: string;\n}\n\nexport function magneticBeamsBackgroundClassNames({\n  className,\n}: MagneticBeamsBackgroundClassNamesOptions = {}) {\n  return cn(magneticBeamsBackgroundRootClasses, className);\n}\n\nexport interface MagneticBeamsBackgroundLayerClassNamesOptions {\n  className?: string;\n  intensity?: MagneticBeamsBackgroundIntensity;\n  tone?: MagneticBeamsBackgroundTone;\n}\n\nexport function magneticBeamsBackgroundLayerClassNames({\n  className,\n  intensity = \"subtle\",\n  tone = \"muted\",\n}: MagneticBeamsBackgroundLayerClassNamesOptions = {}) {\n  return cn(\n    magneticBeamsBackgroundLayerClasses,\n    magneticBeamsBackgroundToneIntensityClasses[tone][intensity],\n    className,\n  );\n}\n\nexport interface MagneticBeamsBackgroundContentClassNamesOptions {\n  className?: string;\n}\n\nexport function magneticBeamsBackgroundContentClassNames({\n  className,\n}: MagneticBeamsBackgroundContentClassNamesOptions = {}) {\n  return cn(magneticBeamsBackgroundContentClasses, className);\n}\n\nconst useIsomorphicLayoutEffect =\n  typeof window === \"undefined\" ? useEffect : useLayoutEffect;\n\nfunction useHasHydrated() {\n  const [hasHydrated, setHasHydrated] = useState(false);\n\n  useIsomorphicLayoutEffect(() => {\n    setHasHydrated(true);\n  }, []);\n\n  return hasHydrated;\n}\n\n// Pauses looping animations while the background is scrolled offscreen.\n// Fails open (stays true) where IntersectionObserver is unavailable so the\n// background still animates in older browsers and jsdom.\nfunction useDecorativeInView(ref: { current: HTMLElement | null }) {\n  const [inView, setInView] = useState(true);\n\n  useEffect(() => {\n    const element = ref.current;\n\n    if (\n      !element ||\n      typeof window === \"undefined\" ||\n      !(\"IntersectionObserver\" in window)\n    ) {\n      return;\n    }\n\n    const observer = new window.IntersectionObserver(([entry]) => {\n      if (entry) {\n        setInView(entry.isIntersecting);\n      }\n    });\n\n    observer.observe(element);\n\n    return () => observer.disconnect();\n  }, [ref]);\n\n  return inView;\n}\n\nfunction railStyle(beam: MagneticBeamsBackgroundBeam): CSSProperties {\n  return beam.axis === \"x\"\n    ? { top: `${beam.position}%` }\n    : { left: `${beam.position}%` };\n}\n\ninterface MagneticBeamsBackgroundBeamElementProps {\n  attracting: boolean;\n  beam: MagneticBeamsBackgroundBeam;\n  mode: MagneticBeamsBackgroundMode;\n  motionConfig: MagneticBeamsBackgroundMotionConfig;\n  pointerFraction: MotionValue<number>;\n  tone: MagneticBeamsBackgroundTone;\n}\n\n// One beam, driven imperatively through a 0..1 pass-progress motion value so\n// the traversal loop, the pointer attraction spring, and the resume pass all\n// hand the same value to each other without visual jumps.\nfunction MagneticBeamsBackgroundBeamElement({\n  attracting,\n  beam,\n  mode,\n  motionConfig,\n  pointerFraction,\n  tone,\n}: MagneticBeamsBackgroundBeamElementProps) {\n  const progress = useMotionValue(0);\n  const translate = useTransform(\n    progress,\n    (value) =>\n      `${magneticBeamsBackgroundTravelStart + value * magneticBeamsBackgroundTravelSpan}%`,\n  );\n\n  useEffect(() => {\n    let controls: AnimationPlaybackControls | undefined;\n    let disposed = false;\n\n    const startLoop = (delay: number) => {\n      controls = animate(progress, [0, 1], {\n        ...motionConfig.transition,\n        delay,\n        repeatDelay: beam.repeatDelay,\n      });\n    };\n\n    if (attracting) {\n      // Magnetic mode re-targets a spring on every pointer move (velocity is\n      // preserved, so the beam glides between targets). Follow mode travels\n      // to each target linearly at the beam's normal pass speed.\n      const attract = (fraction: number) => {\n        const target = getMagneticBeamsBackgroundPointerProgress(fraction);\n\n        controls =\n          mode === \"follow\"\n            ? animate(progress, target, {\n                duration: getMagneticBeamsBackgroundFollowDuration(\n                  progress.get(),\n                  target,\n                  motionConfig.beamDuration,\n                ),\n                ease: \"linear\",\n              })\n            : animate(progress, target, motionConfig.attractTransition);\n      };\n\n      attract(pointerFraction.get());\n      const unsubscribe = pointerFraction.on(\"change\", attract);\n\n      return () => {\n        disposed = true;\n        unsubscribe();\n        controls?.stop();\n      };\n    }\n\n    const current = progress.get();\n\n    if (current > 0) {\n      // Resuming after attraction: finish the current pass at the normal\n      // linear speed from wherever the beam sits, then re-enter the loop.\n      controls = animate(progress, 1, {\n        duration: motionConfig.beamDuration * (1 - current),\n        ease: \"linear\",\n      });\n      void controls.finished.then(() => {\n        if (!disposed) {\n          startLoop(beam.repeatDelay);\n        }\n      });\n    } else {\n      startLoop(beam.delay);\n    }\n\n    return () => {\n      disposed = true;\n      controls?.stop();\n    };\n  }, [attracting, beam, mode, motionConfig, pointerFraction, progress]);\n\n  return (\n    <motionElement.div\n      data-slot=\"magnetic-beams-background-beam\"\n      className={cn(\n        magneticBeamsBackgroundBeamClasses[beam.axis],\n        magneticBeamsBackgroundBeamToneClasses[tone],\n        beam.axis === \"x\" ? \"left-0\" : \"top-0\",\n      )}\n      style={beam.axis === \"x\" ? { x: translate } : { y: translate }}\n    />\n  );\n}\n\nexport const MagneticBeamsBackground = forwardRef(\n  function MagneticBeamsBackground(\n    {\n      animate: animateProp = true,\n      children,\n      className,\n      density = \"normal\",\n      intensity = \"subtle\",\n      interactive = true,\n      mode = \"magnetic\",\n      onPointerLeave: onPointerLeaveProp,\n      onPointerMove: onPointerMoveProp,\n      seed = 1,\n      speed = \"normal\",\n      tone = \"muted\",\n      ...props\n    }: MagneticBeamsBackgroundProps,\n    forwardedRef: ForwardedRef<HTMLDivElement>,\n  ) {\n    const localRef = useRef<HTMLDivElement | null>(null);\n    const composedRef = useCallback(\n      (node: HTMLDivElement | null) => {\n        localRef.current = node;\n\n        if (typeof forwardedRef === \"function\") {\n          forwardedRef(node);\n        } else if (forwardedRef) {\n          forwardedRef.current = node;\n        }\n      },\n      [forwardedRef],\n    );\n\n    const hasHydrated = useHasHydrated();\n    const prefersReducedMotion = useReducedMotion();\n    const inView = useDecorativeInView(localRef);\n    // OS preference is only trusted after hydration so the first client render\n    // matches the server render exactly; animate={false} is deterministic on\n    // both sides and applies immediately.\n    const reducedMotion =\n      !animateProp || (hasHydrated && prefersReducedMotion === true);\n    const pageVisible = usePageVisible();\n    const running = hasHydrated && !reducedMotion && inView && pageVisible;\n\n    const motionConfig = useMemo(\n      () => getMagneticBeamsBackgroundMotionConfig(speed, reducedMotion),\n      [speed, reducedMotion],\n    );\n    const beams = useMemo(\n      () => getMagneticBeamsBackgroundGeometry(seed, density),\n      [seed, density],\n    );\n\n    // Pointer tracking runs through motion values — no React state per pointer\n    // move. Only crossing the enter/leave boundary re-renders, which flips the\n    // beams between their loop and attract modes.\n    const pointerXFraction = useMotionValue(0.5);\n    const pointerYFraction = useMotionValue(0.5);\n    const [pointerActive, setPointerActive] = useState(false);\n\n    const attractEnabled = running && interactive;\n    const attracting = attractEnabled && pointerActive;\n\n    const handlePointerMove = useCallback(\n      (event: ReactPointerEvent<HTMLDivElement>) => {\n        onPointerMoveProp?.(event);\n\n        if (!attractEnabled) {\n          return;\n        }\n\n        const node = localRef.current;\n\n        if (!node) {\n          return;\n        }\n\n        const rect = node.getBoundingClientRect();\n\n        if (rect.width === 0 || rect.height === 0) {\n          return;\n        }\n\n        pointerXFraction.set((event.clientX - rect.left) / rect.width);\n        pointerYFraction.set((event.clientY - rect.top) / rect.height);\n        setPointerActive(true);\n      },\n      [attractEnabled, onPointerMoveProp, pointerXFraction, pointerYFraction],\n    );\n\n    const handlePointerLeave = useCallback(\n      (event: ReactPointerEvent<HTMLDivElement>) => {\n        onPointerLeaveProp?.(event);\n        setPointerActive(false);\n      },\n      [onPointerLeaveProp],\n    );\n\n    return (\n      <MotionConfig reducedMotion={reducedMotion ? \"always\" : \"user\"}>\n        <div\n          {...props}\n          ref={composedRef}\n          data-slot=\"magnetic-beams-background\"\n          data-animate={animateProp ? \"true\" : \"false\"}\n          data-density={density}\n          data-intensity={intensity}\n          data-interactive={interactive ? \"true\" : \"false\"}\n          data-mode={mode}\n          data-speed={speed}\n          data-tone={tone}\n          data-reduced-motion={reducedMotion ? \"true\" : undefined}\n          className={magneticBeamsBackgroundClassNames({ className })}\n          onPointerMove={handlePointerMove}\n          onPointerLeave={handlePointerLeave}\n        >\n          <div\n            aria-hidden=\"true\"\n            data-slot=\"magnetic-beams-background-layer\"\n            className={magneticBeamsBackgroundLayerClassNames({\n              intensity,\n              tone,\n            })}\n          >\n            <div\n              className={cn(\n                magneticBeamsBackgroundPatternClasses,\n                magneticBeamsBackgroundDensityClasses[density],\n              )}\n            />\n            {running\n              ? beams.map((beam, index) => (\n                  <div\n                    key={index}\n                    className={magneticBeamsBackgroundRailClasses[beam.axis]}\n                    style={railStyle(beam)}\n                  >\n                    <MagneticBeamsBackgroundBeamElement\n                      attracting={attracting}\n                      beam={beam}\n                      mode={mode}\n                      motionConfig={motionConfig}\n                      pointerFraction={\n                        beam.axis === \"x\" ? pointerXFraction : pointerYFraction\n                      }\n                      tone={tone}\n                    />\n                  </div>\n                ))\n              : beams.slice(0, 2).map((beam, index) => (\n                  <div\n                    key={index}\n                    className={magneticBeamsBackgroundRailClasses[beam.axis]}\n                    style={railStyle(beam)}\n                  >\n                    <div\n                      data-slot=\"magnetic-beams-background-beam\"\n                      className={cn(\n                        magneticBeamsBackgroundBeamClasses[beam.axis],\n                        magneticBeamsBackgroundBeamToneClasses[tone],\n                        \"opacity-40\",\n                      )}\n                      style={\n                        beam.axis === \"x\"\n                          ? { left: `${beam.staticOffset}%` }\n                          : { top: `${beam.staticOffset}%` }\n                      }\n                    />\n                  </div>\n                ))}\n          </div>\n          <div\n            data-slot=\"magnetic-beams-background-content\"\n            className={magneticBeamsBackgroundContentClassNames()}\n          >\n            {children}\n          </div>\n        </div>\n      </MotionConfig>\n    );\n  },\n);\n\nMagneticBeamsBackground.displayName = \"MagneticBeamsBackground\";\n"
    },
    {
      "path": "packages/components/src/components/magnetic-beams-background/index.ts",
      "type": "registry:file",
      "target": "~/components/dethink/components/magnetic-beams-background/index.ts",
      "content": "export {\n  MagneticBeamsBackground,\n  getMagneticBeamsBackgroundFollowDuration,\n  getMagneticBeamsBackgroundGeometry,\n  getMagneticBeamsBackgroundMotionConfig,\n  getMagneticBeamsBackgroundPointerProgress,\n  magneticBeamsBackgroundClassNames,\n  magneticBeamsBackgroundContentClassNames,\n  magneticBeamsBackgroundLayerClassNames,\n  type MagneticBeamsBackgroundBeam,\n  type MagneticBeamsBackgroundBeamAxis,\n  type MagneticBeamsBackgroundClassNamesOptions,\n  type MagneticBeamsBackgroundContentClassNamesOptions,\n  type MagneticBeamsBackgroundDensity,\n  type MagneticBeamsBackgroundIntensity,\n  type MagneticBeamsBackgroundLayerClassNamesOptions,\n  type MagneticBeamsBackgroundMode,\n  type MagneticBeamsBackgroundMotionConfig,\n  type MagneticBeamsBackgroundProps,\n  type MagneticBeamsBackgroundSpeed,\n  type MagneticBeamsBackgroundTone,\n} from \"./magnetic-beams-background\";\n"
    },
    {
      "path": "packages/components/src/utils/cn.ts",
      "type": "registry:file",
      "target": "~/components/dethink/utils/cn.ts",
      "content": "import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n  return twMerge(clsx(inputs));\n}\n"
    },
    {
      "path": "packages/components/src/utils/seeded-random.ts",
      "type": "registry:file",
      "target": "~/components/dethink/utils/seeded-random.ts",
      "content": "/**\n * Deterministic pseudo-random helpers for decorative components. Background\n * components must render identical markup on the server and every client\n * render, so all \"random\" placement flows through a caller-provided seed\n * instead of Math.random().\n */\nexport function mulberry32(seed: number): () => number {\n  let state = seed >>> 0;\n\n  return () => {\n    state = (state + 0x6d2b79f5) >>> 0;\n    let t = state;\n    t = Math.imul(t ^ (t >>> 15), t | 1);\n    t ^= t + Math.imul(t ^ (t >>> 7), t | 61);\n    return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n  };\n}\n\nexport function seededRange(\n  random: () => number,\n  min: number,\n  max: number,\n): number {\n  return min + (max - min) * random();\n}\n\nexport function seededPick<T>(random: () => number, values: readonly T[]): T {\n  return values[\n    Math.min(values.length - 1, Math.floor(random() * values.length))\n  ];\n}\n"
    }
  ]
}
