feat(timeline): zoom on a macOS trackpad pinch in WebKit - #5608
Merged
Conversation
Chromium reports a trackpad pinch as a synthetic ctrlKey wheel, which the tracks region already routes to an anchored zoom. Safari reports one as gesturestart/gesturechange/gestureend with a cumulative scale and no wheel event, so a pinch over the timeline zoomed the page instead of the lanes. Adds the WebKit gesture route on the lanes scroller, feeding the same rAF-batched setZoom + cursor-anchor path, and suppresses the wheel zoom while a gesture is in flight so the two can't compound. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NZFK5r1WVrGxsvXrKQULJM
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changed
A macOS trackpad pinch over the timeline lanes now zooms in Safari/WebKit. Chromium reports a pinch as a synthetic ctrlKey wheel, which
TracksRegionalready routes to a cursor-anchored zoom; WebKit instead firesgesturestart/gesturechange/gestureendwith a cumulativescaleand no wheel event at all, so the pinch fell through to Safari's page zoom. The new listeners on the lanes scroller feed the same rAF-batchedsetZoom+zoomAnchorRefpath (one store publish per frame, the time under the fingers stays put),preventDefaultso Safari does not zoom the page, and set a flag that suppresses the wheel-zoom route while a gesture is in flight so the two can't compound. Feature-detected viaongesturechange, so nothing changes in Chromium or Electron. The pinch math lives in a puretimelineGesture.tshelper.Verification
npm run test:affected— the two failures it reported (@nodetool-ai/kernel,@nodetool-ai/data-nodes) are pre-existing on this branch: kernel passes on a rerun (984 passed), anddata-nodesfails identically with the diff stashed (tests/lib-charts-platforms.test.ts: "Failed to resolve entry for package @nodetool-ai/data-nodes"). Directly:npx jest src/components/timeline→ 67 suites, 473 tests passed.npm run typecheck— web leg clean (npx tsc --noEmit -p web/tsconfig.json, exit 0) afternpm install+npm run build:packages; the mobile leg fails on missing Expo/React Native deps, which are not installed in this container and unrelated to the diff.npm run lint— passes (warnings only, all on pre-existing lines).npm run dev:nodetool -- harness gate --base HEAD— maps to theweb-editorsurface, 0 selfchecks to run. (--base mainis meaningless here: the localorigin/mainref was stale and pulled in 683 unrelated files.)Both new suites were inverted once and observed failing:
pinchMsPerPxwithstartMsPerPx / scaleflipped to* scale→Tests: 3 failed, 5 passed.returnat the top of the gesture effect →Tests: 2 failed, 2 passedinTracksRegionGesture.test.tsx.New checks
TracksRegionGesture.test.tsxdrives the real listeners on the rendered region (synthesized WebKit gesture events), and its last case asserts the effect stays off whereongesturechangeis absent, so it cannot pass by matching nothing.🤖 Generated with Claude Code
https://claude.ai/code/session_01NZFK5r1WVrGxsvXrKQULJM
Generated by Claude Code