Fix: restore full graph interaction when node detail panel is open

- Remove backdrop inset:0 that was intercepting all canvas touches;
  backdrop is now pointer-events:none and only covers the area above
  the panel (bottom: 55%) so touch events reach the canvas
- Replace blanket `if (panelOpen) return` in handleStart with a guard
  that only skips the two-finger pinch (handled by its own listener),
  restoring single-finger pan, node tap, and pinch-zoom while panel
  is open
- Add user-scalable=no viewport flag to prevent WebView native zoom
  from scaling the panel UI
- Add singleTapTimer for double-tap detection: single tap delays 350ms
  so double-tap is distinguished and only triggers expand/collapse

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Gerwin Kuijntjes
2026-04-02 12:25:09 +02:00
parent c0b3728ceb
commit f37c09a32a

View File

@@ -2,7 +2,7 @@
<html lang="en-US"> <html lang="en-US">
<head> <head>
<title>Graph</title> <title>Graph</title>
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<style> <style>
* { box-sizing: border-box; margin: 0; padding: 0; } * { box-sizing: border-box; margin: 0; padding: 0; }
html, body { width: 100%; height: 100%; overflow: hidden; background: {{backgroundColor}}; } html, body { width: 100%; height: 100%; overflow: hidden; background: {{backgroundColor}}; }
@@ -12,8 +12,12 @@
#panel-backdrop { #panel-backdrop {
display: none; display: none;
position: fixed; position: fixed;
inset: 0; top: 0;
left: 0;
right: 0;
bottom: 55%;
z-index: 90; z-index: 90;
pointer-events: none;
} }
#panel { #panel {
position: fixed; position: fixed;
@@ -921,7 +925,7 @@
} }
// ---- expand / collapse ---- // ---- expand / collapse ----
var lastTapTime = 0, lastTapNode = null; var lastTapTime = 0, lastTapNode = null, singleTapTimer = null;
var expandedBy = {}; // nodeId -> [childIds added by expanding that node] var expandedBy = {}; // nodeId -> [childIds added by expanding that node]
// initial nodes are never collapsible // initial nodes are never collapsible
@@ -1085,7 +1089,7 @@
function handleStart(e) { function handleStart(e) {
if (e.touches) e.preventDefault(); if (e.touches) e.preventDefault();
if (panelOpen) return; // don't start drag while panel is open if (e.touches && e.touches.length >= 2) return; // pinch handled separately
notifyInteraction(); notifyInteraction();
var p = coordFromEvent(e); var p = coordFromEvent(e);
ptrStartX = ptrEndX = p.x; ptrStartX = ptrEndX = p.x;
@@ -1123,16 +1127,24 @@
if (!moved && dt < 300) { if (!moved && dt < 300) {
var hit = nodeAt(ptrStartX, ptrStartY); var hit = nodeAt(ptrStartX, ptrStartY);
var now = Date.now(); var now = Date.now();
// double-tap detection — toggle expand/collapse // double-tap detection — only expand/collapse, no panel
if (hit && hit === lastTapNode && (now - lastTapTime) < 400) { if (hit && hit === lastTapNode && (now - lastTapTime) < 400) {
lastTapNode = null; lastTapTime = 0; lastTapNode = null; lastTapTime = 0;
if (singleTapTimer) { clearTimeout(singleTapTimer); singleTapTimer = null; }
toggleExpandCollapse(hit); toggleExpandCollapse(hit);
} else if (hit) { } else if (hit) {
lastTapNode = hit; lastTapTime = now; lastTapNode = hit; lastTapTime = now;
if (hit === selected && panelOpen) hidePanel(); // Delay panel open to distinguish from double-tap
else showNodePanel(hit); var tappedNode = hit;
if (singleTapTimer) clearTimeout(singleTapTimer);
singleTapTimer = setTimeout(function () {
singleTapTimer = null;
if (tappedNode === selected && panelOpen) hidePanel();
else showNodePanel(tappedNode);
}, 350);
} else { } else {
lastTapNode = null; lastTapTime = 0; lastTapNode = null; lastTapTime = 0;
if (singleTapTimer) { clearTimeout(singleTapTimer); singleTapTimer = null; }
var hitEdge = edgeAt(ptrStartX, ptrStartY); var hitEdge = edgeAt(ptrStartX, ptrStartY);
if (hitEdge) { if (hitEdge) {
if (hitEdge === selectedEdge && panelOpen) hidePanel(); if (hitEdge === selectedEdge && panelOpen) hidePanel();