Merge branch 'main' of ssh://ramanujan.glitch.university:2222/glitch-university/gupi-osint-board
This commit is contained in:
+48
-3
@@ -53,7 +53,9 @@ function ToolRack({ index }: { index: number }) {
|
||||
|
||||
const scene = new THREE.Scene()
|
||||
const camera = new THREE.PerspectiveCamera(34, 1, 0.1, 100)
|
||||
camera.position.set(0.5, 0.45, 3.3)
|
||||
// Pulled back along the same view direction (was z 3.3) so the tools sit smaller in
|
||||
// frame — full-screen models were hard to read as objects.
|
||||
camera.position.set(0.63, 0.57, 8.15)
|
||||
camera.lookAt(0, 0, 0)
|
||||
scene.add(new THREE.AmbientLight(0x40483a, 0.9))
|
||||
const key = new THREE.DirectionalLight(0xfff4e0, 1.1); key.position.set(2, 3, 3); scene.add(key)
|
||||
@@ -63,11 +65,41 @@ function ToolRack({ index }: { index: number }) {
|
||||
const resize = () => { const w = stage.clientWidth, h = stage.clientHeight; if (w && h) { camera.aspect = w / h; camera.updateProjectionMatrix(); renderer.setSize(w, h, false) } }
|
||||
resize(); const ro = new ResizeObserver(resize); ro.observe(stage)
|
||||
|
||||
// Drag to spin: a swipe rotates the holder 1:1 with the pointer and hands its speed
|
||||
// to `spin`, which the idle loop keeps applying and eases back to the resting rate —
|
||||
// so interfering with a flick sends it spinning faster, then it settles.
|
||||
const BASE_SPIN = 0.008
|
||||
let spin = BASE_SPIN
|
||||
let dragging = false
|
||||
let lastX = 0
|
||||
const dom = renderer.domElement
|
||||
dom.style.touchAction = 'none'
|
||||
dom.style.cursor = 'grab'
|
||||
const onDown = (event: PointerEvent) => { dragging = true; lastX = event.clientX; dom.style.cursor = 'grabbing'; dom.setPointerCapture(event.pointerId) }
|
||||
const onMove = (event: PointerEvent) => {
|
||||
if (!dragging) return
|
||||
const delta = (event.clientX - lastX) * 0.01
|
||||
lastX = event.clientX
|
||||
holder.rotation.y += delta
|
||||
spin = THREE.MathUtils.clamp(delta, -0.35, 0.35) // carry the swipe's speed as momentum
|
||||
}
|
||||
const onUp = (event: PointerEvent) => { dragging = false; dom.style.cursor = 'grab'; try { dom.releasePointerCapture(event.pointerId) } catch { /* pointer already released */ } }
|
||||
dom.addEventListener('pointerdown', onDown)
|
||||
dom.addEventListener('pointermove', onMove)
|
||||
dom.addEventListener('pointerup', onUp)
|
||||
dom.addEventListener('pointercancel', onUp)
|
||||
|
||||
let raf = 0
|
||||
const tick = () => { holder.rotation.y += 0.008; renderer.render(scene, camera); raf = requestAnimationFrame(tick) }
|
||||
const tick = () => {
|
||||
if (!dragging) { holder.rotation.y += spin; spin += (BASE_SPIN - spin) * 0.04 } // momentum eases back to idle
|
||||
renderer.render(scene, camera); raf = requestAnimationFrame(tick)
|
||||
}
|
||||
tick()
|
||||
return () => {
|
||||
cancelAnimationFrame(raf); ro.disconnect(); renderer.dispose(); renderer.domElement.remove()
|
||||
cancelAnimationFrame(raf); ro.disconnect()
|
||||
dom.removeEventListener('pointerdown', onDown); dom.removeEventListener('pointermove', onMove)
|
||||
dom.removeEventListener('pointerup', onUp); dom.removeEventListener('pointercancel', onUp)
|
||||
renderer.dispose(); renderer.domElement.remove()
|
||||
scene.traverse(o => { if (o instanceof THREE.Mesh) { o.geometry.dispose(); (o.material as THREE.Material).dispose() } })
|
||||
holderRef.current = null
|
||||
}
|
||||
@@ -89,6 +121,19 @@ export function Inventory({ onClose, session, onTearToBoard }: { onClose?: () =>
|
||||
const [index, setIndex] = useState(0)
|
||||
const [active, setActive] = useState<string | null>(null)
|
||||
|
||||
// Keyboard rack controls: ← / → cycle tools, Space uses the selected one. Inert once a
|
||||
// tool is open, so its own inputs (e.g. the notebook textarea) keep the keys.
|
||||
useEffect(() => {
|
||||
if (active) return
|
||||
const onKey = (event: KeyboardEvent) => {
|
||||
if (event.key === 'ArrowLeft') { event.preventDefault(); setIndex(i => (i - 1 + TOOLS.length) % TOOLS.length) }
|
||||
else if (event.key === 'ArrowRight') { event.preventDefault(); setIndex(i => (i + 1) % TOOLS.length) }
|
||||
else if (event.key === ' ') { event.preventDefault(); setActive(TOOLS[index].key) }
|
||||
}
|
||||
window.addEventListener('keydown', onKey)
|
||||
return () => window.removeEventListener('keydown', onKey)
|
||||
}, [index, active])
|
||||
|
||||
if (active) return <div className="inv-tool">
|
||||
<button className="inv-back" onClick={() => setActive(null)}>‹ TOOLS</button>
|
||||
{onClose && <button className="inv-exit" onClick={onClose}>EXIT ✕</button>}
|
||||
|
||||
Reference in New Issue
Block a user