{
  "name": "desk",
  "title": "Desk",
  "type": "registry:component",
  "description": "A work desk: a top over slim legs, a drawer pedestal, or side panels, with a modesty panel at the back. Base and drawers seeded, each pinnable. One cuboid collider.",
  "dependencies": [
    "@react-three/rapier@^2.2.0",
    "@runek/core@^0.13.0"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "Desk.tsx",
      "content": "import { CuboidCollider, RigidBody } from '@react-three/rapier'\nimport { pick, rng, sub, useWorld, type Vec3, type WorldComponentProps } from '@runek/core'\nimport { useMemo } from 'react'\n\n/** What holds the top up: four slim `legs`, a drawer `pedestal` on one side, or solid side\n *  `panels`. */\nexport type DeskBase = 'legs' | 'pedestal' | 'panels'\n\nexport interface DeskProps extends WorldComponentProps {\n  /** Width along local X, in units. */\n  width?: number\n  /** Depth along local Z, in units. The front, where you sit, faces +Z. */\n  depth?: number\n  /** Height of the work surface, in units. */\n  height?: number\n  /** Seeded when unset. */\n  base?: DeskBase\n  /** Drawers in the pedestal, 0 to 4. Seeded when unset; only a `pedestal` desk has them. */\n  drawers?: number\n  /** Top color; defaults to the palette's `wood`. */\n  color?: string\n  /** Legs, pedestal, and panels; defaults to the palette's `metal` for legs, else `woodDark`. */\n  baseColor?: string\n}\n\ninterface Box {\n  pos: Vec3\n  size: Vec3\n  color: string\n  rough: number\n}\n\nconst BASES: DeskBase[] = ['legs', 'pedestal', 'panels']\n\n/**\n * A work desk: a top over legs, a drawer pedestal, or side panels, with a modesty panel at the back\n * (local -Z). The base and drawer count come from the seed unless you pin them. One cuboid\n * collider for the whole desk. Pair with a `Monitor` on top and a `Chair` at the front.\n */\nexport function Desk({\n  position = [0, 0, 0],\n  rotation = [0, 0, 0],\n  seed = 1,\n  width = 1.4,\n  depth = 0.7,\n  height = 0.75,\n  base,\n  drawers,\n  color,\n  baseColor,\n}: DeskProps) {\n  const { unit, palette } = useWorld()\n  const W = width * unit\n  const D = depth * unit\n  const H = height * unit\n\n  const boxes = useMemo<Box[]>(() => {\n    // Every roll is drawn before overrides apply, so pinning one prop never reshuffles another.\n    const r = rng(sub(seed, 0))\n    const rolled = { base: pick(r, BASES), drawers: pick(r, [2, 3, 3, 4]), left: r() < 0.5 }\n    const kind = base ?? rolled.base\n    const count = Math.min(Math.max(Math.round(drawers ?? rolled.drawers), 0), 4)\n    const pedestalLeft = rolled.left\n    const top = color ?? palette.wood\n    const frame = baseColor ?? (kind === 'legs' ? palette.metal : palette.woodDark)\n    const t = 0.035 * unit\n    const legH = H - t\n    const out: Box[] = [{ pos: [0, H - t / 2, 0], size: [W, t, D], color: top, rough: 0.6 }]\n    const add = (pos: Vec3, size: Vec3, c = frame, rough = 0.7) =>\n      out.push({ pos, size, color: c, rough })\n\n    // Modesty panel across the back, up from knee height.\n    const panelH = legH * 0.55\n    add([0, legH - panelH / 2, -D / 2 + 0.03 * unit], [W - 0.08 * unit, panelH, 0.018 * unit])\n\n    const side = (sx: number) => {\n      const th = 0.03 * unit\n      add([sx * (W / 2 - th / 2 - 0.01 * unit), legH / 2, 0], [th, legH, D - 0.02 * unit])\n    }\n    if (kind === 'legs') {\n      const leg = 0.04 * unit\n      for (const sx of [-1, 1])\n        for (const sz of [-1, 1])\n          add(\n            [sx * (W / 2 - leg / 2 - 0.03 * unit), legH / 2, sz * (D / 2 - leg / 2 - 0.03 * unit)],\n            [leg, legH, leg],\n            frame,\n            0.4,\n          )\n    } else if (kind === 'panels') {\n      side(-1)\n      side(1)\n    } else {\n      // A drawer pedestal on one side, a panel leg on the other.\n      const sx = pedestalLeft ? -1 : 1\n      const pw = Math.min(0.42 * unit, W * 0.36)\n      const px = sx * (W / 2 - pw / 2 - 0.01 * unit)\n      const pd = D - 0.06 * unit\n      add([px, legH / 2, -0.02 * unit], [pw, legH, pd])\n      side(-sx)\n      if (count > 0) {\n        const gap = 0.012 * unit\n        const fh = (legH - 0.06 * unit - gap * (count + 1)) / count\n        for (let i = 0; i < count; i++) {\n          const y = 0.06 * unit + gap * (i + 1) + fh * (i + 0.5)\n          const z = -0.02 * unit + pd / 2\n          add([px, y, z + 0.006 * unit], [pw - 0.03 * unit, fh, 0.012 * unit], top, 0.6)\n          add(\n            [px, y + fh * 0.28, z + 0.02 * unit],\n            [pw * 0.4, 0.014 * unit, 0.014 * unit],\n            palette.metal,\n            0.35,\n          )\n        }\n      }\n    }\n    return out\n  }, [seed, base, drawers, color, baseColor, palette, unit, W, D, H])\n\n  return (\n    <RigidBody type=\"fixed\" colliders={false} position={position} rotation={rotation}>\n      <CuboidCollider args={[W / 2, H / 2, D / 2]} position={[0, H / 2, 0]} />\n      {boxes.map((b) => (\n        <mesh\n          key={`${b.pos[0].toFixed(3)}:${b.pos[1].toFixed(3)}:${b.pos[2].toFixed(3)}`}\n          position={b.pos}\n          castShadow\n          receiveShadow\n        >\n          <boxGeometry args={b.size} />\n          <meshStandardMaterial color={b.color} roughness={b.rough} />\n        </mesh>\n      ))}\n    </RigidBody>\n  )\n}\n\nDesk.groundSitting = true\n",
      "type": "registry:component"
    }
  ]
}
