[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M73C01RQ6FW7YAQ48BDWMJS2":3,"skill-same:01M73C01RQ6FW7YAQ48BDWMJS2":46},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":19,"linkOnly":20,"markdownContent":21,"markdownBody":22,"frontMatter":23,"refName":24,"commitSha":25,"skillDirectory":26,"forksCount":14,"repositoryHtmlUrl":27,"repositoryHomepageUrl":6,"ownerHtmlUrl":28,"ownerBlogUrl":29,"ownerDescription":30,"updatedAt":31,"install":32,"resources":45},"01M73C01RQ6FW7YAQ48BDWMJS2","threejs-interaction",null,"Three.js interaction - raycasting, controls, mouse\u002Ftouch input, object selection. Use when handling user input, implementing click detection, adding camera controls, or creating interactive 3D experiences.","kalash33\u002Fkstack","kalash33","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F90259149?v=4","01M7365X8Q0B73NYHA730SMZWN","Kalash Poddar","01M7365X9D0JK47C3Q0YCCK9Z2",0,"MIT","https:\u002F\u002Fgithub.com\u002Fkalash33\u002Fkstack\u002Fblob\u002Fmain\u002Fskills\u002Fthreejs-interaction\u002FSKILL.md",[],[],"2026-11-11 15:11:14",false,"---\nname: threejs-interaction\ndescription: Three.js interaction - raycasting, controls, mouse\u002Ftouch input, object selection. Use when handling user input, implementing click detection, adding camera controls, or creating interactive 3D experiences.\n---\n\n# Three.js Interaction\n\n## Quick Start\n\n```javascript\nimport * as THREE from \"three\";\nimport { OrbitControls } from \"three\u002Faddons\u002Fcontrols\u002FOrbitControls.js\";\n\n\u002F\u002F Camera controls\nconst controls = new OrbitControls(camera, renderer.domElement);\ncontrols.enableDamping = true;\n\n\u002F\u002F Raycasting for click detection\nconst raycaster = new THREE.Raycaster();\nconst mouse = new THREE.Vector2();\n\nfunction onClick(event) {\n  mouse.x = (event.clientX \u002F window.innerWidth) * 2 - 1;\n  mouse.y = -(event.clientY \u002F window.innerHeight) * 2 + 1;\n\n  raycaster.setFromCamera(mouse, camera);\n  const intersects = raycaster.intersectObjects(scene.children);\n\n  if (intersects.length > 0) {\n    console.log(\"Clicked:\", intersects[0].object);\n  }\n}\n\nwindow.addEventListener(\"click\", onClick);\n```\n\n## Raycaster\n\n### Basic Raycasting\n\n```javascript\nconst raycaster = new THREE.Raycaster();\n\n\u002F\u002F From camera (mouse picking)\nraycaster.setFromCamera(mousePosition, camera);\n\n\u002F\u002F From any origin and direction\nraycaster.set(origin, direction); \u002F\u002F origin: Vector3, direction: normalized Vector3\n\n\u002F\u002F Get intersections\nconst intersects = raycaster.intersectObjects(objects, recursive);\n\n\u002F\u002F intersects array contains:\n\u002F\u002F {\n\u002F\u002F   distance: number,          \u002F\u002F Distance from ray origin\n\u002F\u002F   point: Vector3,            \u002F\u002F Intersection point in world coords\n\u002F\u002F   face: Face3,               \u002F\u002F Intersected face\n\u002F\u002F   faceIndex: number,         \u002F\u002F Face index\n\u002F\u002F   object: Object3D,          \u002F\u002F Intersected object\n\u002F\u002F   uv: Vector2,               \u002F\u002F UV coordinates at intersection\n\u002F\u002F   uv1: Vector2,              \u002F\u002F Second UV channel\n\u002F\u002F   normal: Vector3,           \u002F\u002F Interpolated face normal\n\u002F\u002F   instanceId: number         \u002F\u002F For InstancedMesh\n\u002F\u002F }\n```\n\n### Mouse Position Conversion\n\n```javascript\nconst mouse = new THREE.Vector2();\n\nfunction updateMouse(event) {\n  \u002F\u002F For full window\n  mouse.x = (event.clientX \u002F window.innerWidth) * 2 - 1;\n  mouse.y = -(event.clientY \u002F window.innerHeight) * 2 + 1;\n}\n\n\u002F\u002F For specific canvas element\nfunction updateMouseCanvas(event, canvas) {\n  const rect = canvas.getBoundingClientRect();\n  mouse.x = ((event.clientX - rect.left) \u002F rect.width) * 2 - 1;\n  mouse.y = -((event.clientY - rect.top) \u002F rect.height) * 2 + 1;\n}\n```\n\n### Touch Support\n\n```javascript\nfunction onTouchStart(event) {\n  event.preventDefault();\n\n  if (event.touches.length === 1) {\n    const touch = event.touches[0];\n    mouse.x = (touch.clientX \u002F window.innerWidth) * 2 - 1;\n    mouse.y = -(touch.clientY \u002F window.innerHeight) * 2 + 1;\n\n    raycaster.setFromCamera(mouse, camera);\n    const intersects = raycaster.intersectObjects(clickableObjects);\n\n    if (intersects.length > 0) {\n      handleSelection(intersects[0]);\n    }\n  }\n}\n\nrenderer.domElement.addEventListener(\"touchstart\", onTouchStart);\n```\n\n### Raycaster Options\n\n```javascript\nconst raycaster = new THREE.Raycaster();\n\n\u002F\u002F Near\u002Ffar clipping (default: 0, Infinity)\nraycaster.near = 0;\nraycaster.far = 100;\n\n\u002F\u002F Line\u002FPoints precision\nraycaster.params.Line.threshold = 0.1;\nraycaster.params.Points.threshold = 0.1;\n\n\u002F\u002F Layers (only intersect objects on specific layers)\nraycaster.layers.set(1);\n```\n\n### Efficient Raycasting\n\n```javascript\n\u002F\u002F Only check specific objects\nconst clickables = [mesh1, mesh2, mesh3];\nconst intersects = raycaster.intersectObjects(clickables, false);\n\n\u002F\u002F Use layers for filtering\nmesh1.layers.set(1); \u002F\u002F Clickable layer\nraycaster.layers.set(1);\n\n\u002F\u002F Throttle raycast for hover effects\nlet lastRaycast = 0;\nfunction onMouseMove(event) {\n  const now = Date.now();\n  if (now - lastRaycast \u003C 50) return; \u002F\u002F 20fps max\n  lastRaycast = now;\n\n  \u002F\u002F Raycast here\n}\n```\n\n## Camera Controls\n\n### OrbitControls\n\n```javascript\nimport { OrbitControls } from \"three\u002Faddons\u002Fcontrols\u002FOrbitControls.js\";\n\nconst controls = new OrbitControls(camera, renderer.domElement);\n\n\u002F\u002F Damping (smooth movement)\ncontrols.enableDamping = true;\ncontrols.dampingFactor = 0.05;\n\n\u002F\u002F Rotation limits\ncontrols.minPolarAngle = 0; \u002F\u002F Top\ncontrols.maxPolarAngle = Math.PI \u002F 2; \u002F\u002F Horizon\ncontrols.minAzimuthAngle = -Math.PI \u002F 4; \u002F\u002F Left\ncontrols.maxAzimuthAngle = Math.PI \u002F 4; \u002F\u002F Right\n\n\u002F\u002F Zoom limits\ncontrols.minDistance = 2;\ncontrols.maxDistance = 50;\n\n\u002F\u002F Enable\u002Fdisable features\ncontrols.enableRotate = true;\ncontrols.enableZoom = true;\ncontrols.enablePan = true;\n\n\u002F\u002F Auto-rotate\ncontrols.autoRotate = true;\ncontrols.autoRotateSpeed = 2.0;\n\n\u002F\u002F Target (orbit point)\ncontrols.target.set(0, 1, 0);\n\n\u002F\u002F Update in animation loop\nfunction animate() {\n  controls.update(); \u002F\u002F Required for damping and auto-rotate\n  renderer.render(scene, camera);\n}\n```\n\n### FlyControls\n\n```javascript\nimport { FlyControls } from \"three\u002Faddons\u002Fcontrols\u002FFlyControls.js\";\n\nconst controls = new FlyControls(camera, renderer.domElement);\ncontrols.movementSpeed = 10;\ncontrols.rollSpeed = Math.PI \u002F 24;\ncontrols.dragToLook = true;\n\n\u002F\u002F Update with delta\nfunction animate() {\n  controls.update(clock.getDelta());\n  renderer.render(scene, camera);\n}\n```\n\n### FirstPersonControls\n\n```javascript\nimport { FirstPersonControls } from \"three\u002Faddons\u002Fcontrols\u002FFirstPersonControls.js\";\n\nconst controls = new FirstPersonControls(camera, renderer.domElement);\ncontrols.movementSpeed = 10;\ncontrols.lookSpeed = 0.1;\ncontrols.lookVertical = true;\ncontrols.constrainVertical = true;\ncontrols.verticalMin = Math.PI \u002F 4;\ncontrols.verticalMax = (Math.PI * 3) \u002F 4;\n\nfunction animate() {\n  controls.update(clock.getDelta());\n}\n```\n\n### PointerLockControls\n\n```javascript\nimport { PointerLockControls } from \"three\u002Faddons\u002Fcontrols\u002FPointerLockControls.js\";\n\nconst controls = new PointerLockControls(camera, document.body);\n\n\u002F\u002F Lock pointer on click\ndocument.addEventListener(\"click\", () => {\n  controls.lock();\n});\n\ncontrols.addEventListener(\"lock\", () => {\n  console.log(\"Pointer locked\");\n});\n\ncontrols.addEventListener(\"unlock\", () => {\n  console.log(\"Pointer unlocked\");\n});\n\n\u002F\u002F Movement\nconst velocity = new THREE.Vector3();\nconst direction = new THREE.Vector3();\nconst moveForward = false;\nconst moveBackward = false;\n\ndocument.addEventListener(\"keydown\", (event) => {\n  switch (event.code) {\n    case \"KeyW\":\n      moveForward = true;\n      break;\n    case \"KeyS\":\n      moveBackward = true;\n      break;\n  }\n});\n\nfunction animate() {\n  if (controls.isLocked) {\n    direction.z = Number(moveForward) - Number(moveBackward);\n    direction.normalize();\n\n    velocity.z -= direction.z * 0.1;\n    velocity.z *= 0.9; \u002F\u002F Friction\n\n    controls.moveForward(-velocity.z);\n  }\n}\n```\n\n### TrackballControls\n\n```javascript\nimport { TrackballControls } from \"three\u002Faddons\u002Fcontrols\u002FTrackballControls.js\";\n\nconst controls = new TrackballControls(camera, renderer.domElement);\ncontrols.rotateSpeed = 2.0;\ncontrols.zoomSpeed = 1.2;\ncontrols.panSpeed = 0.8;\ncontrols.staticMoving = true;\n\nfunction animate() {\n  controls.update();\n}\n```\n\n### MapControls\n\n```javascript\nimport { MapControls } from \"three\u002Faddons\u002Fcontrols\u002FMapControls.js\";\n\nconst controls = new MapControls(camera, renderer.domElement);\ncontrols.enableDamping = true;\ncontrols.dampingFactor = 0.05;\ncontrols.screenSpacePanning = false;\ncontrols.maxPolarAngle = Math.PI \u002F 2;\n```\n\n## TransformControls\n\nGizmo for moving\u002Frotating\u002Fscaling objects.\n\n```javascript\nimport { TransformControls } from \"three\u002Faddons\u002Fcontrols\u002FTransformControls.js\";\n\nconst transformControls = new TransformControls(camera, renderer.domElement);\nscene.add(transformControls);\n\n\u002F\u002F Attach to object\ntransformControls.attach(selectedMesh);\n\n\u002F\u002F Switch modes\ntransformControls.setMode(\"translate\"); \u002F\u002F 'translate', 'rotate', 'scale'\n\n\u002F\u002F Change space\ntransformControls.setSpace(\"local\"); \u002F\u002F 'local', 'world'\n\n\u002F\u002F Size\ntransformControls.setSize(1);\n\n\u002F\u002F Events\ntransformControls.addEventListener(\"dragging-changed\", (event) => {\n  \u002F\u002F Disable orbit controls while dragging\n  orbitControls.enabled = !event.value;\n});\n\ntransformControls.addEventListener(\"change\", () => {\n  renderer.render(scene, camera);\n});\n\n\u002F\u002F Keyboard shortcuts\nwindow.addEventListener(\"keydown\", (event) => {\n  switch (event.key) {\n    case \"g\":\n      transformControls.setMode(\"translate\");\n      break;\n    case \"r\":\n      transformControls.setMode(\"rotate\");\n      break;\n    case \"s\":\n      transformControls.setMode(\"scale\");\n      break;\n    case \"Escape\":\n      transformControls.detach();\n      break;\n  }\n});\n```\n\n## DragControls\n\nDrag objects directly.\n\n```javascript\nimport { DragControls } from \"three\u002Faddons\u002Fcontrols\u002FDragControls.js\";\n\nconst draggableObjects = [mesh1, mesh2, mesh3];\nconst dragControls = new DragControls(\n  draggableObjects,\n  camera,\n  renderer.domElement,\n);\n\ndragControls.addEventListener(\"dragstart\", (event) => {\n  orbitControls.enabled = false;\n  event.object.material.emissive.set(0xaaaaaa);\n});\n\ndragControls.addEventListener(\"drag\", (event) => {\n  \u002F\u002F Constrain to ground plane\n  event.object.position.y = 0;\n});\n\ndragControls.addEventListener(\"dragend\", (event) => {\n  orbitControls.enabled = true;\n  event.object.material.emissive.set(0x000000);\n});\n```\n\n## Selection System\n\n### Click to Select\n\n```javascript\nconst raycaster = new THREE.Raycaster();\nconst mouse = new THREE.Vector2();\nlet selectedObject = null;\n\nfunction onMouseDown(event) {\n  mouse.x = (event.clientX \u002F window.innerWidth) * 2 - 1;\n  mouse.y = -(event.clientY \u002F window.innerHeight) * 2 + 1;\n\n  raycaster.setFromCamera(mouse, camera);\n  const intersects = raycaster.intersectObjects(selectableObjects);\n\n  \u002F\u002F Deselect previous\n  if (selectedObject) {\n    selectedObject.material.emissive.set(0x000000);\n  }\n\n  \u002F\u002F Select new\n  if (intersects.length > 0) {\n    selectedObject = intersects[0].object;\n    selectedObject.material.emissive.set(0x444444);\n  } else {\n    selectedObject = null;\n  }\n}\n```\n\n### Box Selection\n\n```javascript\nimport { SelectionBox } from \"three\u002Faddons\u002Finteractive\u002FSelectionBox.js\";\nimport { SelectionHelper } from \"three\u002Faddons\u002Finteractive\u002FSelectionHelper.js\";\n\nconst selectionBox = new SelectionBox(camera, scene);\nconst selectionHelper = new SelectionHelper(renderer, \"selectBox\"); \u002F\u002F CSS class\n\ndocument.addEventListener(\"pointerdown\", (event) => {\n  selectionBox.startPoint.set(\n    (event.clientX \u002F window.innerWidth) * 2 - 1,\n    -(event.clientY \u002F window.innerHeight) * 2 + 1,\n    0.5,\n  );\n});\n\ndocument.addEventListener(\"pointermove\", (event) => {\n  if (selectionHelper.isDown) {\n    selectionBox.endPoint.set(\n      (event.clientX \u002F window.innerWidth) * 2 - 1,\n      -(event.clientY \u002F window.innerHeight) * 2 + 1,\n      0.5,\n    );\n  }\n});\n\ndocument.addEventListener(\"pointerup\", (event) => {\n  selectionBox.endPoint.set(\n    (event.clientX \u002F window.innerWidth) * 2 - 1,\n    -(event.clientY \u002F window.innerHeight) * 2 + 1,\n    0.5,\n  );\n\n  const selected = selectionBox.select();\n  console.log(\"Selected objects:\", selected);\n});\n```\n\n### Hover Effects\n\n```javascript\nconst raycaster = new THREE.Raycaster();\nconst mouse = new THREE.Vector2();\nlet hoveredObject = null;\n\nfunction onMouseMove(event) {\n  mouse.x = (event.clientX \u002F window.innerWidth) * 2 - 1;\n  mouse.y = -(event.clientY \u002F window.innerHeight) * 2 + 1;\n\n  raycaster.setFromCamera(mouse, camera);\n  const intersects = raycaster.intersectObjects(hoverableObjects);\n\n  \u002F\u002F Reset previous hover\n  if (hoveredObject) {\n    hoveredObject.material.color.set(hoveredObject.userData.originalColor);\n    document.body.style.cursor = \"default\";\n  }\n\n  \u002F\u002F Apply new hover\n  if (intersects.length > 0) {\n    hoveredObject = intersects[0].object;\n    if (!hoveredObject.userData.originalColor) {\n      hoveredObject.userData.originalColor =\n        hoveredObject.material.color.getHex();\n    }\n    hoveredObject.material.color.set(0xff6600);\n    document.body.style.cursor = \"pointer\";\n  } else {\n    hoveredObject = null;\n  }\n}\n\nwindow.addEventListener(\"mousemove\", onMouseMove);\n```\n\n## Keyboard Input\n\n```javascript\nconst keys = {};\n\ndocument.addEventListener(\"keydown\", (event) => {\n  keys[event.code] = true;\n});\n\ndocument.addEventListener(\"keyup\", (event) => {\n  keys[event.code] = false;\n});\n\nfunction update() {\n  const speed = 0.1;\n\n  if (keys[\"KeyW\"]) player.position.z -= speed;\n  if (keys[\"KeyS\"]) player.position.z += speed;\n  if (keys[\"KeyA\"]) player.position.x -= speed;\n  if (keys[\"KeyD\"]) player.position.x += speed;\n  if (keys[\"Space\"]) player.position.y += speed;\n  if (keys[\"ShiftLeft\"]) player.position.y -= speed;\n}\n```\n\n## World-Screen Coordinate Conversion\n\n### World to Screen\n\n```javascript\nfunction worldToScreen(position, camera) {\n  const vector = position.clone();\n  vector.project(camera);\n\n  return {\n    x: ((vector.x + 1) \u002F 2) * window.innerWidth,\n    y: (-(vector.y - 1) \u002F 2) * window.innerHeight,\n  };\n}\n\n\u002F\u002F Position HTML element over 3D object\nconst screenPos = worldToScreen(mesh.position, camera);\nelement.style.left = screenPos.x + \"px\";\nelement.style.top = screenPos.y + \"px\";\n```\n\n### Screen to World\n\n```javascript\nfunction screenToWorld(screenX, screenY, camera, targetZ = 0) {\n  const vector = new THREE.Vector3(\n    (screenX \u002F window.innerWidth) * 2 - 1,\n    -(screenY \u002F window.innerHeight) * 2 + 1,\n    0.5,\n  );\n\n  vector.unproject(camera);\n\n  const dir = vector.sub(camera.position).normalize();\n  const distance = (targetZ - camera.position.z) \u002F dir.z;\n\n  return camera.position.clone().add(dir.multiplyScalar(distance));\n}\n```\n\n### Ray-Plane Intersection\n\n```javascript\nfunction getRayPlaneIntersection(mouse, camera, plane) {\n  const raycaster = new THREE.Raycaster();\n  raycaster.setFromCamera(mouse, camera);\n\n  const intersection = new THREE.Vector3();\n  raycaster.ray.intersectPlane(plane, intersection);\n\n  return intersection;\n}\n\n\u002F\u002F Ground plane\nconst groundPlane = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0);\nconst worldPos = getRayPlaneIntersection(mouse, camera, groundPlane);\n```\n\n## Event Handling Best Practices\n\n```javascript\nclass InteractionManager {\n  constructor(camera, renderer, scene) {\n    this.camera = camera;\n    this.renderer = renderer;\n    this.scene = scene;\n    this.raycaster = new THREE.Raycaster();\n    this.mouse = new THREE.Vector2();\n    this.clickables = [];\n\n    this.bindEvents();\n  }\n\n  bindEvents() {\n    const canvas = this.renderer.domElement;\n\n    canvas.addEventListener(\"click\", (e) => this.onClick(e));\n    canvas.addEventListener(\"mousemove\", (e) => this.onMouseMove(e));\n    canvas.addEventListener(\"touchstart\", (e) => this.onTouchStart(e));\n  }\n\n  updateMouse(event) {\n    const rect = this.renderer.domElement.getBoundingClientRect();\n    this.mouse.x = ((event.clientX - rect.left) \u002F rect.width) * 2 - 1;\n    this.mouse.y = -((event.clientY - rect.top) \u002F rect.height) * 2 + 1;\n  }\n\n  getIntersects() {\n    this.raycaster.setFromCamera(this.mouse, this.camera);\n    return this.raycaster.intersectObjects(this.clickables, true);\n  }\n\n  onClick(event) {\n    this.updateMouse(event);\n    const intersects = this.getIntersects();\n\n    if (intersects.length > 0) {\n      const object = intersects[0].object;\n      if (object.userData.onClick) {\n        object.userData.onClick(intersects[0]);\n      }\n    }\n  }\n\n  addClickable(object, callback) {\n    this.clickables.push(object);\n    object.userData.onClick = callback;\n  }\n\n  dispose() {\n    \u002F\u002F Remove event listeners\n  }\n}\n\n\u002F\u002F Usage\nconst interaction = new InteractionManager(camera, renderer, scene);\ninteraction.addClickable(mesh, (intersect) => {\n  console.log(\"Clicked at:\", intersect.point);\n});\n```\n\n## Performance Tips\n\n1. **Limit raycasts**: Throttle mousemove handlers\n2. **Use layers**: Filter raycast targets\n3. **Simple collision meshes**: Use invisible simpler geometry for raycasting\n4. **Disable controls when not needed**: `controls.enabled = false`\n5. **Batch updates**: Group interaction checks\n\n```javascript\n\u002F\u002F Use simpler geometry for raycasting\nconst complexMesh = loadedModel;\nconst collisionMesh = new THREE.Mesh(\n  new THREE.BoxGeometry(1, 1, 1),\n  new THREE.MeshBasicMaterial({ visible: false }),\n);\ncollisionMesh.userData.target = complexMesh;\nclickables.push(collisionMesh);\n```\n\n## See Also\n\n- `threejs-fundamentals` - Camera and scene setup\n- `threejs-animation` - Animating interactions\n- `threejs-shaders` - Visual feedback effects\n","# Three.js Interaction\n\n## Quick Start\n\n```javascript\nimport * as THREE from \"three\";\nimport { OrbitControls } from \"three\u002Faddons\u002Fcontrols\u002FOrbitControls.js\";\n\n\u002F\u002F Camera controls\nconst controls = new OrbitControls(camera, renderer.domElement);\ncontrols.enableDamping = true;\n\n\u002F\u002F Raycasting for click detection\nconst raycaster = new THREE.Raycaster();\nconst mouse = new THREE.Vector2();\n\nfunction onClick(event) {\n  mouse.x = (event.clientX \u002F window.innerWidth) * 2 - 1;\n  mouse.y = -(event.clientY \u002F window.innerHeight) * 2 + 1;\n\n  raycaster.setFromCamera(mouse, camera);\n  const intersects = raycaster.intersectObjects(scene.children);\n\n  if (intersects.length > 0) {\n    console.log(\"Clicked:\", intersects[0].object);\n  }\n}\n\nwindow.addEventListener(\"click\", onClick);\n```\n\n## Raycaster\n\n### Basic Raycasting\n\n```javascript\nconst raycaster = new THREE.Raycaster();\n\n\u002F\u002F From camera (mouse picking)\nraycaster.setFromCamera(mousePosition, camera);\n\n\u002F\u002F From any origin and direction\nraycaster.set(origin, direction); \u002F\u002F origin: Vector3, direction: normalized Vector3\n\n\u002F\u002F Get intersections\nconst intersects = raycaster.intersectObjects(objects, recursive);\n\n\u002F\u002F intersects array contains:\n\u002F\u002F {\n\u002F\u002F   distance: number,          \u002F\u002F Distance from ray origin\n\u002F\u002F   point: Vector3,            \u002F\u002F Intersection point in world coords\n\u002F\u002F   face: Face3,               \u002F\u002F Intersected face\n\u002F\u002F   faceIndex: number,         \u002F\u002F Face index\n\u002F\u002F   object: Object3D,          \u002F\u002F Intersected object\n\u002F\u002F   uv: Vector2,               \u002F\u002F UV coordinates at intersection\n\u002F\u002F   uv1: Vector2,              \u002F\u002F Second UV channel\n\u002F\u002F   normal: Vector3,           \u002F\u002F Interpolated face normal\n\u002F\u002F   instanceId: number         \u002F\u002F For InstancedMesh\n\u002F\u002F }\n```\n\n### Mouse Position Conversion\n\n```javascript\nconst mouse = new THREE.Vector2();\n\nfunction updateMouse(event) {\n  \u002F\u002F For full window\n  mouse.x = (event.clientX \u002F window.innerWidth) * 2 - 1;\n  mouse.y = -(event.clientY \u002F window.innerHeight) * 2 + 1;\n}\n\n\u002F\u002F For specific canvas element\nfunction updateMouseCanvas(event, canvas) {\n  const rect = canvas.getBoundingClientRect();\n  mouse.x = ((event.clientX - rect.left) \u002F rect.width) * 2 - 1;\n  mouse.y = -((event.clientY - rect.top) \u002F rect.height) * 2 + 1;\n}\n```\n\n### Touch Support\n\n```javascript\nfunction onTouchStart(event) {\n  event.preventDefault();\n\n  if (event.touches.length === 1) {\n    const touch = event.touches[0];\n    mouse.x = (touch.clientX \u002F window.innerWidth) * 2 - 1;\n    mouse.y = -(touch.clientY \u002F window.innerHeight) * 2 + 1;\n\n    raycaster.setFromCamera(mouse, camera);\n    const intersects = raycaster.intersectObjects(clickableObjects);\n\n    if (intersects.length > 0) {\n      handleSelection(intersects[0]);\n    }\n  }\n}\n\nrenderer.domElement.addEventListener(\"touchstart\", onTouchStart);\n```\n\n### Raycaster Options\n\n```javascript\nconst raycaster = new THREE.Raycaster();\n\n\u002F\u002F Near\u002Ffar clipping (default: 0, Infinity)\nraycaster.near = 0;\nraycaster.far = 100;\n\n\u002F\u002F Line\u002FPoints precision\nraycaster.params.Line.threshold = 0.1;\nraycaster.params.Points.threshold = 0.1;\n\n\u002F\u002F Layers (only intersect objects on specific layers)\nraycaster.layers.set(1);\n```\n\n### Efficient Raycasting\n\n```javascript\n\u002F\u002F Only check specific objects\nconst clickables = [mesh1, mesh2, mesh3];\nconst intersects = raycaster.intersectObjects(clickables, false);\n\n\u002F\u002F Use layers for filtering\nmesh1.layers.set(1); \u002F\u002F Clickable layer\nraycaster.layers.set(1);\n\n\u002F\u002F Throttle raycast for hover effects\nlet lastRaycast = 0;\nfunction onMouseMove(event) {\n  const now = Date.now();\n  if (now - lastRaycast \u003C 50) return; \u002F\u002F 20fps max\n  lastRaycast = now;\n\n  \u002F\u002F Raycast here\n}\n```\n\n## Camera Controls\n\n### OrbitControls\n\n```javascript\nimport { OrbitControls } from \"three\u002Faddons\u002Fcontrols\u002FOrbitControls.js\";\n\nconst controls = new OrbitControls(camera, renderer.domElement);\n\n\u002F\u002F Damping (smooth movement)\ncontrols.enableDamping = true;\ncontrols.dampingFactor = 0.05;\n\n\u002F\u002F Rotation limits\ncontrols.minPolarAngle = 0; \u002F\u002F Top\ncontrols.maxPolarAngle = Math.PI \u002F 2; \u002F\u002F Horizon\ncontrols.minAzimuthAngle = -Math.PI \u002F 4; \u002F\u002F Left\ncontrols.maxAzimuthAngle = Math.PI \u002F 4; \u002F\u002F Right\n\n\u002F\u002F Zoom limits\ncontrols.minDistance = 2;\ncontrols.maxDistance = 50;\n\n\u002F\u002F Enable\u002Fdisable features\ncontrols.enableRotate = true;\ncontrols.enableZoom = true;\ncontrols.enablePan = true;\n\n\u002F\u002F Auto-rotate\ncontrols.autoRotate = true;\ncontrols.autoRotateSpeed = 2.0;\n\n\u002F\u002F Target (orbit point)\ncontrols.target.set(0, 1, 0);\n\n\u002F\u002F Update in animation loop\nfunction animate() {\n  controls.update(); \u002F\u002F Required for damping and auto-rotate\n  renderer.render(scene, camera);\n}\n```\n\n### FlyControls\n\n```javascript\nimport { FlyControls } from \"three\u002Faddons\u002Fcontrols\u002FFlyControls.js\";\n\nconst controls = new FlyControls(camera, renderer.domElement);\ncontrols.movementSpeed = 10;\ncontrols.rollSpeed = Math.PI \u002F 24;\ncontrols.dragToLook = true;\n\n\u002F\u002F Update with delta\nfunction animate() {\n  controls.update(clock.getDelta());\n  renderer.render(scene, camera);\n}\n```\n\n### FirstPersonControls\n\n```javascript\nimport { FirstPersonControls } from \"three\u002Faddons\u002Fcontrols\u002FFirstPersonControls.js\";\n\nconst controls = new FirstPersonControls(camera, renderer.domElement);\ncontrols.movementSpeed = 10;\ncontrols.lookSpeed = 0.1;\ncontrols.lookVertical = true;\ncontrols.constrainVertical = true;\ncontrols.verticalMin = Math.PI \u002F 4;\ncontrols.verticalMax = (Math.PI * 3) \u002F 4;\n\nfunction animate() {\n  controls.update(clock.getDelta());\n}\n```\n\n### PointerLockControls\n\n```javascript\nimport { PointerLockControls } from \"three\u002Faddons\u002Fcontrols\u002FPointerLockControls.js\";\n\nconst controls = new PointerLockControls(camera, document.body);\n\n\u002F\u002F Lock pointer on click\ndocument.addEventListener(\"click\", () => {\n  controls.lock();\n});\n\ncontrols.addEventListener(\"lock\", () => {\n  console.log(\"Pointer locked\");\n});\n\ncontrols.addEventListener(\"unlock\", () => {\n  console.log(\"Pointer unlocked\");\n});\n\n\u002F\u002F Movement\nconst velocity = new THREE.Vector3();\nconst direction = new THREE.Vector3();\nconst moveForward = false;\nconst moveBackward = false;\n\ndocument.addEventListener(\"keydown\", (event) => {\n  switch (event.code) {\n    case \"KeyW\":\n      moveForward = true;\n      break;\n    case \"KeyS\":\n      moveBackward = true;\n      break;\n  }\n});\n\nfunction animate() {\n  if (controls.isLocked) {\n    direction.z = Number(moveForward) - Number(moveBackward);\n    direction.normalize();\n\n    velocity.z -= direction.z * 0.1;\n    velocity.z *= 0.9; \u002F\u002F Friction\n\n    controls.moveForward(-velocity.z);\n  }\n}\n```\n\n### TrackballControls\n\n```javascript\nimport { TrackballControls } from \"three\u002Faddons\u002Fcontrols\u002FTrackballControls.js\";\n\nconst controls = new TrackballControls(camera, renderer.domElement);\ncontrols.rotateSpeed = 2.0;\ncontrols.zoomSpeed = 1.2;\ncontrols.panSpeed = 0.8;\ncontrols.staticMoving = true;\n\nfunction animate() {\n  controls.update();\n}\n```\n\n### MapControls\n\n```javascript\nimport { MapControls } from \"three\u002Faddons\u002Fcontrols\u002FMapControls.js\";\n\nconst controls = new MapControls(camera, renderer.domElement);\ncontrols.enableDamping = true;\ncontrols.dampingFactor = 0.05;\ncontrols.screenSpacePanning = false;\ncontrols.maxPolarAngle = Math.PI \u002F 2;\n```\n\n## TransformControls\n\nGizmo for moving\u002Frotating\u002Fscaling objects.\n\n```javascript\nimport { TransformControls } from \"three\u002Faddons\u002Fcontrols\u002FTransformControls.js\";\n\nconst transformControls = new TransformControls(camera, renderer.domElement);\nscene.add(transformControls);\n\n\u002F\u002F Attach to object\ntransformControls.attach(selectedMesh);\n\n\u002F\u002F Switch modes\ntransformControls.setMode(\"translate\"); \u002F\u002F 'translate', 'rotate', 'scale'\n\n\u002F\u002F Change space\ntransformControls.setSpace(\"local\"); \u002F\u002F 'local', 'world'\n\n\u002F\u002F Size\ntransformControls.setSize(1);\n\n\u002F\u002F Events\ntransformControls.addEventListener(\"dragging-changed\", (event) => {\n  \u002F\u002F Disable orbit controls while dragging\n  orbitControls.enabled = !event.value;\n});\n\ntransformControls.addEventListener(\"change\", () => {\n  renderer.render(scene, camera);\n});\n\n\u002F\u002F Keyboard shortcuts\nwindow.addEventListener(\"keydown\", (event) => {\n  switch (event.key) {\n    case \"g\":\n      transformControls.setMode(\"translate\");\n      break;\n    case \"r\":\n      transformControls.setMode(\"rotate\");\n      break;\n    case \"s\":\n      transformControls.setMode(\"scale\");\n      break;\n    case \"Escape\":\n      transformControls.detach();\n      break;\n  }\n});\n```\n\n## DragControls\n\nDrag objects directly.\n\n```javascript\nimport { DragControls } from \"three\u002Faddons\u002Fcontrols\u002FDragControls.js\";\n\nconst draggableObjects = [mesh1, mesh2, mesh3];\nconst dragControls = new DragControls(\n  draggableObjects,\n  camera,\n  renderer.domElement,\n);\n\ndragControls.addEventListener(\"dragstart\", (event) => {\n  orbitControls.enabled = false;\n  event.object.material.emissive.set(0xaaaaaa);\n});\n\ndragControls.addEventListener(\"drag\", (event) => {\n  \u002F\u002F Constrain to ground plane\n  event.object.position.y = 0;\n});\n\ndragControls.addEventListener(\"dragend\", (event) => {\n  orbitControls.enabled = true;\n  event.object.material.emissive.set(0x000000);\n});\n```\n\n## Selection System\n\n### Click to Select\n\n```javascript\nconst raycaster = new THREE.Raycaster();\nconst mouse = new THREE.Vector2();\nlet selectedObject = null;\n\nfunction onMouseDown(event) {\n  mouse.x = (event.clientX \u002F window.innerWidth) * 2 - 1;\n  mouse.y = -(event.clientY \u002F window.innerHeight) * 2 + 1;\n\n  raycaster.setFromCamera(mouse, camera);\n  const intersects = raycaster.intersectObjects(selectableObjects);\n\n  \u002F\u002F Deselect previous\n  if (selectedObject) {\n    selectedObject.material.emissive.set(0x000000);\n  }\n\n  \u002F\u002F Select new\n  if (intersects.length > 0) {\n    selectedObject = intersects[0].object;\n    selectedObject.material.emissive.set(0x444444);\n  } else {\n    selectedObject = null;\n  }\n}\n```\n\n### Box Selection\n\n```javascript\nimport { SelectionBox } from \"three\u002Faddons\u002Finteractive\u002FSelectionBox.js\";\nimport { SelectionHelper } from \"three\u002Faddons\u002Finteractive\u002FSelectionHelper.js\";\n\nconst selectionBox = new SelectionBox(camera, scene);\nconst selectionHelper = new SelectionHelper(renderer, \"selectBox\"); \u002F\u002F CSS class\n\ndocument.addEventListener(\"pointerdown\", (event) => {\n  selectionBox.startPoint.set(\n    (event.clientX \u002F window.innerWidth) * 2 - 1,\n    -(event.clientY \u002F window.innerHeight) * 2 + 1,\n    0.5,\n  );\n});\n\ndocument.addEventListener(\"pointermove\", (event) => {\n  if (selectionHelper.isDown) {\n    selectionBox.endPoint.set(\n      (event.clientX \u002F window.innerWidth) * 2 - 1,\n      -(event.clientY \u002F window.innerHeight) * 2 + 1,\n      0.5,\n    );\n  }\n});\n\ndocument.addEventListener(\"pointerup\", (event) => {\n  selectionBox.endPoint.set(\n    (event.clientX \u002F window.innerWidth) * 2 - 1,\n    -(event.clientY \u002F window.innerHeight) * 2 + 1,\n    0.5,\n  );\n\n  const selected = selectionBox.select();\n  console.log(\"Selected objects:\", selected);\n});\n```\n\n### Hover Effects\n\n```javascript\nconst raycaster = new THREE.Raycaster();\nconst mouse = new THREE.Vector2();\nlet hoveredObject = null;\n\nfunction onMouseMove(event) {\n  mouse.x = (event.clientX \u002F window.innerWidth) * 2 - 1;\n  mouse.y = -(event.clientY \u002F window.innerHeight) * 2 + 1;\n\n  raycaster.setFromCamera(mouse, camera);\n  const intersects = raycaster.intersectObjects(hoverableObjects);\n\n  \u002F\u002F Reset previous hover\n  if (hoveredObject) {\n    hoveredObject.material.color.set(hoveredObject.userData.originalColor);\n    document.body.style.cursor = \"default\";\n  }\n\n  \u002F\u002F Apply new hover\n  if (intersects.length > 0) {\n    hoveredObject = intersects[0].object;\n    if (!hoveredObject.userData.originalColor) {\n      hoveredObject.userData.originalColor =\n        hoveredObject.material.color.getHex();\n    }\n    hoveredObject.material.color.set(0xff6600);\n    document.body.style.cursor = \"pointer\";\n  } else {\n    hoveredObject = null;\n  }\n}\n\nwindow.addEventListener(\"mousemove\", onMouseMove);\n```\n\n## Keyboard Input\n\n```javascript\nconst keys = {};\n\ndocument.addEventListener(\"keydown\", (event) => {\n  keys[event.code] = true;\n});\n\ndocument.addEventListener(\"keyup\", (event) => {\n  keys[event.code] = false;\n});\n\nfunction update() {\n  const speed = 0.1;\n\n  if (keys[\"KeyW\"]) player.position.z -= speed;\n  if (keys[\"KeyS\"]) player.position.z += speed;\n  if (keys[\"KeyA\"]) player.position.x -= speed;\n  if (keys[\"KeyD\"]) player.position.x += speed;\n  if (keys[\"Space\"]) player.position.y += speed;\n  if (keys[\"ShiftLeft\"]) player.position.y -= speed;\n}\n```\n\n## World-Screen Coordinate Conversion\n\n### World to Screen\n\n```javascript\nfunction worldToScreen(position, camera) {\n  const vector = position.clone();\n  vector.project(camera);\n\n  return {\n    x: ((vector.x + 1) \u002F 2) * window.innerWidth,\n    y: (-(vector.y - 1) \u002F 2) * window.innerHeight,\n  };\n}\n\n\u002F\u002F Position HTML element over 3D object\nconst screenPos = worldToScreen(mesh.position, camera);\nelement.style.left = screenPos.x + \"px\";\nelement.style.top = screenPos.y + \"px\";\n```\n\n### Screen to World\n\n```javascript\nfunction screenToWorld(screenX, screenY, camera, targetZ = 0) {\n  const vector = new THREE.Vector3(\n    (screenX \u002F window.innerWidth) * 2 - 1,\n    -(screenY \u002F window.innerHeight) * 2 + 1,\n    0.5,\n  );\n\n  vector.unproject(camera);\n\n  const dir = vector.sub(camera.position).normalize();\n  const distance = (targetZ - camera.position.z) \u002F dir.z;\n\n  return camera.position.clone().add(dir.multiplyScalar(distance));\n}\n```\n\n### Ray-Plane Intersection\n\n```javascript\nfunction getRayPlaneIntersection(mouse, camera, plane) {\n  const raycaster = new THREE.Raycaster();\n  raycaster.setFromCamera(mouse, camera);\n\n  const intersection = new THREE.Vector3();\n  raycaster.ray.intersectPlane(plane, intersection);\n\n  return intersection;\n}\n\n\u002F\u002F Ground plane\nconst groundPlane = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0);\nconst worldPos = getRayPlaneIntersection(mouse, camera, groundPlane);\n```\n\n## Event Handling Best Practices\n\n```javascript\nclass InteractionManager {\n  constructor(camera, renderer, scene) {\n    this.camera = camera;\n    this.renderer = renderer;\n    this.scene = scene;\n    this.raycaster = new THREE.Raycaster();\n    this.mouse = new THREE.Vector2();\n    this.clickables = [];\n\n    this.bindEvents();\n  }\n\n  bindEvents() {\n    const canvas = this.renderer.domElement;\n\n    canvas.addEventListener(\"click\", (e) => this.onClick(e));\n    canvas.addEventListener(\"mousemove\", (e) => this.onMouseMove(e));\n    canvas.addEventListener(\"touchstart\", (e) => this.onTouchStart(e));\n  }\n\n  updateMouse(event) {\n    const rect = this.renderer.domElement.getBoundingClientRect();\n    this.mouse.x = ((event.clientX - rect.left) \u002F rect.width) * 2 - 1;\n    this.mouse.y = -((event.clientY - rect.top) \u002F rect.height) * 2 + 1;\n  }\n\n  getIntersects() {\n    this.raycaster.setFromCamera(this.mouse, this.camera);\n    return this.raycaster.intersectObjects(this.clickables, true);\n  }\n\n  onClick(event) {\n    this.updateMouse(event);\n    const intersects = this.getIntersects();\n\n    if (intersects.length > 0) {\n      const object = intersects[0].object;\n      if (object.userData.onClick) {\n        object.userData.onClick(intersects[0]);\n      }\n    }\n  }\n\n  addClickable(object, callback) {\n    this.clickables.push(object);\n    object.userData.onClick = callback;\n  }\n\n  dispose() {\n    \u002F\u002F Remove event listeners\n  }\n}\n\n\u002F\u002F Usage\nconst interaction = new InteractionManager(camera, renderer, scene);\ninteraction.addClickable(mesh, (intersect) => {\n  console.log(\"Clicked at:\", intersect.point);\n});\n```\n\n## Performance Tips\n\n1. **Limit raycasts**: Throttle mousemove handlers\n2. **Use layers**: Filter raycast targets\n3. **Simple collision meshes**: Use invisible simpler geometry for raycasting\n4. **Disable controls when not needed**: `controls.enabled = false`\n5. **Batch updates**: Group interaction checks\n\n```javascript\n\u002F\u002F Use simpler geometry for raycasting\nconst complexMesh = loadedModel;\nconst collisionMesh = new THREE.Mesh(\n  new THREE.BoxGeometry(1, 1, 1),\n  new THREE.MeshBasicMaterial({ visible: false }),\n);\ncollisionMesh.userData.target = complexMesh;\nclickables.push(collisionMesh);\n```\n\n## See Also\n\n- `threejs-fundamentals` - Camera and scene setup\n- `threejs-animation` - Animating interactions\n- `threejs-shaders` - Visual feedback effects\n",{"name":5,"description":7},"main","b30820b3248f6676d5389f0ccabd4e5250c9c536","skills\u002Fthreejs-interaction","https:\u002F\u002Fgithub.com\u002Fkalash33\u002Fkstack","https:\u002F\u002Fgithub.com\u002Fkalash33","kalashpoddar.me","Tech-savvy Full Stack Developer 🚀 | Cybersecurity enthusiast 🔒 | Passionate about innovation and securing the digital world 🌐 | Let's code securely! 💻🔐","2026-05-20 04:11:22",{"source":8,"skillName":5,"commands":33,"prompt":43,"zipAvailable":44},[34,37,40],{"tool":35,"command":36},"npx","npx skills add kalash33\u002Fkstack --skill threejs-interaction",{"tool":38,"command":39},"bunx","bunx skills add kalash33\u002Fkstack --skill threejs-interaction",{"tool":41,"command":42},"pnpm","pnpm dlx skills add kalash33\u002Fkstack --skill threejs-interaction","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fkalash33\u002Fkstack\u002Fblob\u002Fmain\u002Fskills\u002Fthreejs-interaction\u002FSKILL.md",true,[],[47,54,61,68,75,82],{"skillNo":48,"name":49,"nameZh":6,"description":50,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":51,"categories":52,"keywords":53,"publishedAt":19},"01M73CFERCR43JCAP606E41EFB","zustand-store-ts","Create Zustand stores with TypeScript, subscribeWithSelector middleware, and proper state\u002Faction separation. Use when building React state management, creating global stores, or implementing reacti...","https:\u002F\u002Fgithub.com\u002Fkalash33\u002Fkstack\u002Fblob\u002Fmain\u002Fskills\u002Fzustand-store-ts\u002FSKILL.md",[],[],{"skillNo":55,"name":56,"nameZh":6,"description":57,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":58,"categories":59,"keywords":60,"publishedAt":19},"01M73CFCMFVTAQEMTZ5HR10X8V","zoom-automation","Automate Zoom meeting creation, management, recordings, webinars, and participant tracking via Rube MCP (Composio). Always search tools first for current schemas.","https:\u002F\u002Fgithub.com\u002Fkalash33\u002Fkstack\u002Fblob\u002Fmain\u002Fskills\u002Fzoom-automation\u002FSKILL.md",[],[],{"skillNo":62,"name":63,"nameZh":6,"description":64,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":65,"categories":66,"keywords":67,"publishedAt":19},"01M73CFAA7EBWXJVXDQ1X555MB","zoho-crm-automation","Automate Zoho CRM tasks via Rube MCP (Composio): create\u002Fupdate records, search contacts, manage leads, and convert leads. Always search tools first for current schemas.","https:\u002F\u002Fgithub.com\u002Fkalash33\u002Fkstack\u002Fblob\u002Fmain\u002Fskills\u002Fzoho-crm-automation\u002FSKILL.md",[],[],{"skillNo":69,"name":70,"nameZh":6,"description":71,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":72,"categories":73,"keywords":74,"publishedAt":19},"01M73CF80XCM9K81567MJ8QZFW","zod-validation-expert","Expert in Zod — TypeScript-first schema validation. Covers parsing, custom errors, refinements, type inference, and integration with React Hook Form, Next.js, and tRPC.","https:\u002F\u002Fgithub.com\u002Fkalash33\u002Fkstack\u002Fblob\u002Fmain\u002Fskills\u002Fzod-validation-expert\u002FSKILL.md",[],[],{"skillNo":76,"name":77,"nameZh":6,"description":78,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":79,"categories":80,"keywords":81,"publishedAt":19},"01M73CF4YPMF3RA8ZJDXZSMD93","zeroize-audit","Detects missing zeroization of sensitive data in source code and identifies zeroization removed by compiler optimizations, with assembly-level analysis, and control-flow verification. Use for auditing C\u002FC++\u002FRust code handling secrets, keys, passwords, or other sensitive data.","https:\u002F\u002Fgithub.com\u002Fkalash33\u002Fkstack\u002Fblob\u002Fmain\u002Fskills\u002Fzeroize-audit\u002FSKILL.md",[],[],{"skillNo":83,"name":84,"nameZh":6,"description":85,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":86,"categories":87,"keywords":88,"publishedAt":19},"01M73CF2S25EN3XNAMKY47XHPS","zendesk-automation","Automate Zendesk tasks via Rube MCP (Composio): tickets, users, organizations, replies. Always search tools first for current schemas.","https:\u002F\u002Fgithub.com\u002Fkalash33\u002Fkstack\u002Fblob\u002Fmain\u002Fskills\u002Fzendesk-automation\u002FSKILL.md",[],[]]