diff --git a/vault.html b/vault.html index aca2edf..ea0e421 100644 --- a/vault.html +++ b/vault.html @@ -13,7 +13,8 @@ --accent: #FFD700; --link: #FFD700; --border: #313244; --green: #a6e3a1; --red: #f38ba8; --blue: #89b4fa; --teal: #94e2d5; } -* { margin: 0; padding: 0; box-sizing: border-box; } +* { margin: 0; padding: 0; box-sizing: border-box; -webkit-tap-highlight-color: transparent; } +html, body { touch-action: manipulation; } body { background: var(--bg-primary); color: var(--text-primary); font-family: -apple-system, 'Segoe UI', Inter, sans-serif; height: 100vh; overflow: hidden; } .vault-app { @@ -47,7 +48,8 @@ body { background: var(--bg-primary); color: var(--text-primary); font-family: - /* Sidebar file tree */ .vault-sidebar { background: var(--bg-secondary); border-right: 1px solid var(--border); - overflow-y: auto; padding: 8px 0; font-size: 13px; + overflow-y: auto; -webkit-overflow-scrolling: touch; + touch-action: pan-y; padding: 8px 0; font-size: 13px; } .tree-folder { cursor: pointer; user-select: none; } .tree-folder-header { @@ -68,7 +70,8 @@ body { background: var(--bg-primary); color: var(--text-primary); font-family: - /* Content area */ .vault-content { - overflow-y: auto; padding: 32px 48px; max-width: 900px; + overflow-y: auto; -webkit-overflow-scrolling: touch; + touch-action: pan-y; padding: 32px 48px; max-width: 900px; line-height: 1.7; font-size: 15px; } .vault-content h1 { color: var(--accent); font-size: 28px; margin: 24px 0 12px; } @@ -94,9 +97,9 @@ body { background: var(--bg-primary); color: var(--text-primary); font-family: - /* Graph panel */ .vault-graph { background: var(--bg-secondary); border-left: 1px solid var(--border); - position: relative; overflow: hidden; + position: relative; overflow: hidden; touch-action: none; } -.vault-graph canvas { width: 100% !important; height: 100% !important; } +.vault-graph canvas { width: 100% !important; height: 100% !important; touch-action: none; } /* Search results overlay */ #search-results { @@ -321,23 +324,65 @@ async function loadGraph() { graphScene.add(new THREE.Line(geo, mat)); }); - // Orbit + // Orbit (mouse + touch support) let isDrag = false, lastX = 0, lastY = 0, rotX = 0, rotY = 0; - canvas.addEventListener('mousedown', e => { isDrag = true; lastX = e.clientX; lastY = e.clientY; }); - canvas.addEventListener('mousemove', e => { + function getPos(e) { + if (e.touches && e.touches.length) { + return { x: e.touches[0].clientX, y: e.touches[0].clientY }; + } + return { x: e.clientX, y: e.clientY }; + } + function onDragStart(e) { + e.preventDefault(); + isDrag = true; + const p = getPos(e); + lastX = p.x; lastY = p.y; + } + function onDragMove(e) { if (!isDrag) return; - rotY += (e.clientX - lastX) * 0.005; - rotX += (e.clientY - lastY) * 0.005; - lastX = e.clientX; lastY = e.clientY; - }); - canvas.addEventListener('mouseup', () => isDrag = false); - canvas.addEventListener('wheel', e => { + e.preventDefault(); + const p = getPos(e); + rotY += (p.x - lastX) * 0.005; + rotX += (p.y - lastY) * 0.005; + lastX = p.x; lastY = p.y; + } + function onDragEnd(e) { + if (e && e.preventDefault) e.preventDefault(); + isDrag = false; + } + function onWheel(e) { graphCamera.position.z += e.deltaY * 0.5; graphCamera.position.z = Math.max(50, Math.min(800, graphCamera.position.z)); - }); + } + canvas.addEventListener('mousedown', onDragStart); + canvas.addEventListener('mousemove', onDragMove); + window.addEventListener('mouseup', onDragEnd); + canvas.addEventListener('touchstart', onDragStart, { passive: false }); + canvas.addEventListener('touchmove', onDragMove, { passive: false }); + canvas.addEventListener('touchend', onDragEnd, { passive: false }); + canvas.addEventListener('touchcancel', onDragEnd, { passive: false }); + canvas.addEventListener('wheel', onWheel, { passive: true }); + + // Auto-rotate: when user is not interacting, slow rotation. + // Pauses when tab is hidden, when user is dragging, and after page visibility loss. + let autoRotate = true; + let lastInteractionTime = 0; + const AUTO_ROTATE_RESUME_MS = 3000; // resume auto-rotation 3s after user stops dragging function animateGraph() { requestAnimationFrame(animateGraph); + if (document.hidden) return; // skip rendering when tab is in background + const now = Date.now(); + const userJustInteracted = (now - lastInteractionTime) < AUTO_ROTATE_RESUME_MS; + if (isDrag) { + lastInteractionTime = now; + autoRotate = false; + } else if (!userJustInteracted) { + autoRotate = true; + } + if (autoRotate && !isDrag) { + rotY += 0.002; + } graphScene.rotation.y = rotY; graphScene.rotation.x = rotX; graphRenderer.render(graphScene, graphCamera);