{
  "name": "ceilinglight",
  "title": "CeilingLight",
  "type": "registry:component",
  "description": "A ceiling fitting hanging from its origin: a flush dome, a pendant shade on a cord, or an aimed spot can. Can light only after dusk.",
  "dependencies": [
    "@runek/core@^0.15.0",
    "three@^0.184.0"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "CeilingLight.tsx",
      "content": "import { pick, range, rng, sub, useSun, useWorld, type WorldComponentProps } from '@runek/core'\nimport { useMemo } from 'react'\nimport { Object3D } from 'three'\n\n/** Close to the ceiling (`flush`), hanging on a cord (`pendant`), or an aimed can (`spot`). */\nexport type CeilingLightKind = 'flush' | 'pendant' | 'spot'\n/** The pendant's shade. */\nexport type CeilingLightShade = 'dome' | 'cone' | 'drum' | 'globe'\n\nexport interface CeilingLightProps extends WorldComponentProps {\n  /** Seeded when unset. */\n  kind?: CeilingLightKind\n  /** Pendant only; seeded when unset. */\n  shade?: CeilingLightShade\n  /** Pendant cord length from the ceiling to the shade, in units. */\n  drop?: number\n  /** Shade size across, in units. */\n  size?: number\n  /** Canopy, cord, and can; defaults to the palette's `metal`. */\n  color?: string\n  /** The shade. Seeded when unset. */\n  shadeColor?: string\n  /** The light and the glowing bulb. */\n  lightColor?: string\n  /** Light strength. */\n  intensity?: number\n  /** Switched on. */\n  on?: boolean\n  /** Light only while the world's sun is down. */\n  nightOnly?: boolean\n}\n\nconst KINDS: CeilingLightKind[] = ['flush', 'pendant', 'pendant', 'spot']\nconst SHADES: CeilingLightShade[] = ['dome', 'cone', 'drum', 'globe']\nconst SHADE_COLORS = ['#e9e4d8', '#2f3a35', '#b8743f', '#d9c27a', '#1f2a44']\n\n/**\n * A ceiling fitting, its origin at the ceiling with the fitting hanging below (local -Y): a\n * frosted `flush` dome, a `pendant` shade on a cord, or a `spot` can throwing a cone of light\n * down. `nightOnly` switches it on with the world's dusk. Decorative: no collider.\n */\nexport function CeilingLight({\n  position = [0, 0, 0],\n  rotation = [0, 0, 0],\n  seed = 1,\n  kind,\n  shade,\n  drop = 0.6,\n  size = 0.36,\n  color,\n  shadeColor,\n  lightColor = '#ffe2b8',\n  intensity = 12,\n  on = true,\n  nightOnly = false,\n}: CeilingLightProps) {\n  const { unit, palette } = useWorld()\n  const sun = useSun()\n  const lit = on && (!nightOnly || !sun.day)\n  const metal = color ?? palette.metal\n  const R = (size / 2) * unit\n\n  const look = useMemo(() => {\n    const r = rng(sub(seed, 0))\n    return {\n      kind: kind ?? pick(r, KINDS),\n      shade: shade ?? pick(r, SHADES),\n      shadeColor: shadeColor ?? pick(r, SHADE_COLORS),\n      tilt: range(r, -0.25, 0.25),\n    }\n  }, [seed, kind, shade, shadeColor])\n\n  const target = useMemo(() => new Object3D(), [])\n  const bulb = (\n    <meshStandardMaterial\n      color=\"#fff6e0\"\n      emissive={lightColor}\n      emissiveIntensity={lit ? 2 : 0}\n      toneMapped={!lit}\n    />\n  )\n  const canopy = (\n    <mesh position={[0, -0.015 * unit, 0]}>\n      <cylinderGeometry args={[0.06 * unit, 0.07 * unit, 0.03 * unit, 16]} />\n      <meshStandardMaterial color={metal} metalness={0.5} roughness={0.4} />\n    </mesh>\n  )\n\n  if (look.kind === 'flush') {\n    return (\n      <group position={position} rotation={rotation}>\n        <mesh position={[0, -0.01 * unit, 0]}>\n          <cylinderGeometry args={[R * 1.05, R * 1.05, 0.02 * unit, 28]} />\n          <meshStandardMaterial color={metal} metalness={0.5} roughness={0.4} />\n        </mesh>\n        <mesh position={[0, -0.02 * unit, 0]} scale={[1, 0.45, 1]} rotation={[Math.PI, 0, 0]}>\n          <sphereGeometry args={[R, 28, 12, 0, Math.PI * 2, 0, Math.PI / 2]} />\n          {bulb}\n        </mesh>\n        {lit && (\n          <pointLight\n            position={[0, -R * 0.6, 0]}\n            color={lightColor}\n            intensity={intensity}\n            distance={10 * unit}\n            decay={2}\n          />\n        )}\n      </group>\n    )\n  }\n\n  if (look.kind === 'spot') {\n    const canL = 0.14 * unit\n    return (\n      <group position={position} rotation={rotation}>\n        {canopy}\n        <group position={[0, -0.04 * unit, 0]} rotation={[look.tilt, 0, 0]}>\n          <mesh position={[0, -canL / 2, 0]} castShadow>\n            <cylinderGeometry args={[0.05 * unit, 0.045 * unit, canL, 18, 1, true]} />\n            <meshStandardMaterial color={metal} metalness={0.5} roughness={0.4} side={2} />\n          </mesh>\n          <mesh position={[0, -canL + 0.01 * unit, 0]} rotation={[Math.PI / 2, 0, 0]}>\n            <circleGeometry args={[0.04 * unit, 18]} />\n            {bulb}\n          </mesh>\n          <primitive object={target} position={[0, -3 * unit, 0]} />\n          {lit && (\n            <spotLight\n              position={[0, -canL, 0]}\n              target={target}\n              color={lightColor}\n              intensity={intensity * 2}\n              distance={12 * unit}\n              angle={0.5}\n              penumbra={0.6}\n              decay={2}\n            />\n          )}\n        </group>\n      </group>\n    )\n  }\n\n  const D = drop * unit\n  const dome = look.shade === 'dome'\n  const shadeH = dome ? R : (look.shade === 'drum' ? 0.7 : 0.8) * R * 2\n  return (\n    <group position={position} rotation={rotation}>\n      {canopy}\n      <mesh position={[0, -D / 2, 0]}>\n        <cylinderGeometry args={[0.003 * unit, 0.003 * unit, D, 5]} />\n        <meshStandardMaterial color={metal} />\n      </mesh>\n      <group position={[0, -D, 0]}>\n        {look.shade === 'globe' ? (\n          <mesh position={[0, -R, 0]}>\n            <sphereGeometry args={[R, 24, 16]} />\n            <meshStandardMaterial\n              color={look.shadeColor}\n              emissive={lightColor}\n              emissiveIntensity={lit ? 0.9 : 0}\n              transparent\n              opacity={0.92}\n            />\n          </mesh>\n        ) : (\n          <mesh position={[0, dome ? -R : -shadeH / 2, 0]} castShadow>\n            {dome ? (\n              <sphereGeometry args={[R, 24, 10, 0, Math.PI * 2, 0, Math.PI / 2]} />\n            ) : look.shade === 'cone' ? (\n              <cylinderGeometry args={[R * 0.12, R, shadeH, 24, 1, true]} />\n            ) : (\n              <cylinderGeometry args={[R, R, shadeH, 24, 1, true]} />\n            )}\n            <meshStandardMaterial\n              color={look.shadeColor}\n              roughness={0.5}\n              metalness={0.2}\n              side={2}\n            />\n          </mesh>\n        )}\n        {look.shade !== 'globe' && (\n          <mesh position={[0, -shadeH * 0.8, 0]}>\n            <sphereGeometry args={[0.035 * unit, 12, 8]} />\n            {bulb}\n          </mesh>\n        )}\n        {lit && (\n          <pointLight\n            position={[0, -shadeH, 0]}\n            color={lightColor}\n            intensity={intensity}\n            distance={10 * unit}\n            decay={2}\n          />\n        )}\n      </group>\n    </group>\n  )\n}\n",
      "type": "registry:component"
    }
  ]
}
