Skip to content

feat: add zoom slider and trackpad panning#985

Open
AkshitBitsian356u wants to merge 10 commits into
CircuitVerse:mainfrom
AkshitBitsian356u:main
Open

feat: add zoom slider and trackpad panning#985
AkshitBitsian356u wants to merge 10 commits into
CircuitVerse:mainfrom
AkshitBitsian356u:main

Conversation

@AkshitBitsian356u

@AkshitBitsian356u AkshitBitsian356u commented Feb 26, 2026

Copy link
Copy Markdown

Screenshots/Video of the UI changes -

2026-03-01.23-56-41.mp4

Fix #987

Changes :

  • Added zoom slider and trackpad panning support for
    intuitive canvas navigation.
  • Replaced zoom slider with +/- buttons in QuickButton
    components for cleaner zoom UX.
  • Improved canvas pan/zoom robustness and event handling
    to prevent conflicting interactions.
  • Normalized zoom delta calculations and validated slider
    inputs for consistent zoom behavior.
  • Fixed horizontal/vertical panning direction in
    applyCanvasPan and added inline documentation for
    wheel/trackpad event handling.
  • Added findDimensions call for minimap setup and
    refactored zoom scale constants in applyZoomChange.
  • Added devicePixelRatio constant (DPR) for consistent
    scaling across devices, fixing blank simulator screen.

Code Understanding and AI Usage

Did you use AI assistance (ChatGPT, Claude, Copilot, etc.) to write any part of this code?

  • No, I wrote all the code myself
  • Yes, I used AI assistance (continue below)

If you used AI assistance:

  • I have reviewed every single line of the AI-generated or AI-suggested code
  • I can explain the purpose and logic of each function/component I added
  • I have tested edge cases and understand how the code handles them
  • I have modified the AI output to follow this project's coding standards and conventions

Explain your implementation approach:

  • The main goal of this change is to make moving around the circuit canvas more natural by using panning gestures instead of the older “hand swapping” interaction.
  • I updated the interaction layer so that panning now works through pointer/wheel events (for example, trackpad two‑finger drag) that translate into changes in the canvas offset.
  • The panning logic reuses the existing camera/viewport state, so zoom level and panning stay consistent across different input methods.
  • I verified that the existing zoom slider behaviour remains unchanged and that zooming and panning can be combined smoothly without conflicting events.
  • I used AI assistance to explore alternative ways of handling the gesture events and to think through potential edge cases (fast scrolling, panning at high zoom, and when the canvas is near its bounds). All final decisions and code changes were made by me, and I tested the interactions directly in the simulator.

Checklist before requesting a review

  • I have added proper PR title and linked to the issue (or marked it as N/A)
  • I have performed a self-review of my code
  • I can explain the purpose of every function, class, and logic block I added
  • I understand why my changes work and have tested them thoroughly on the simulator
  • I have considered potential edge cases and how my code handles them (e.g., high zoom, fast panning)
  • If it is a core feature, I have added thorough tests
  • My code follows the project's style guidelines and conventions

Note: Please check Allow edits from maintainers if you would like us to assist in the PR.

Summary by CodeRabbit

  • New Features

    • Slider replaced by simplified discrete zoom controls (± buttons) with accessible titles.
  • Improvements

    • Expanded keyboard zoom shortcuts (Cmd/Ctrl + +/= and -).
    • Mouse wheel/trackpad now pans the canvas instead of zooming; minimap briefly appears during pan.
    • Visual, hover and focus updates for zoom controls and improved accessibility.
    • Added stable pan/zoom helpers to ensure consistent behavior across inputs.

Copilot AI review requested due to automatic review settings February 26, 2026 22:38
@netlify

netlify Bot commented Feb 26, 2026

Copy link
Copy Markdown

Deploy Preview for circuitverse ready!

Name Link
🔨 Latest commit d7322e7
🔍 Latest deploy log https://app.netlify.com/projects/circuitverse/deploys/6a1bf4e6347af500097b4881
😎 Deploy Preview https://deploy-preview-985--circuitverse.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
Lighthouse
Lighthouse
1 paths audited
Performance: 44 (🟢 up 12 from production)
Accessibility: 77 (🟢 up 7 from production)
Best Practices: 92 (no change from production)
SEO: 82 (no change from production)
PWA: -
View the detailed breakdown and full score reports
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai

coderabbitai Bot commented Feb 26, 2026

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Navbar zoom controls were replaced with discrete increment/decrement buttons in desktop and mobile QuickButton components instead of a range slider. Wheel/trackpad events now perform canvas panning using cross‑browser delta extraction (extractScrollDelta) and applyCanvasPan, showing the minimap briefly during pans; wheel no longer changes zoom. Keyboard zoom accepts Cmd/Ctrl plus +/= to zoom in and - to zoom out. A unified applyZoomChange was added, and helper functions setZoomFromSlider and getZoomSliderValue were introduced. A comment in engine.js was revised.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main changes: zoom slider UI is now accompanied by +/- buttons and trackpad panning is added to the event listeners, matching the core objectives of the PR.
Docstring Coverage ✅ Passed Docstring coverage is 83.33% which is sufficient. The required threshold is 80.00%.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request adds trackpad panning support to the circuit simulator canvas and refactors the zoom controls from a jQuery-based implementation to a Vue-based implementation. The changes modify how users navigate the canvas by replacing scroll-to-zoom with scroll-to-pan behavior, while keeping zoom controls accessible via keyboard shortcuts and UI slider buttons.

Changes:

  • Changed mouse wheel/trackpad scroll events from zooming to panning the canvas
  • Replaced jQuery-based zoom slider implementation with Vue-based reactive zoom controls in QuickButton and QuickButtonMobile components
  • Added keyboard shortcut enhancements for zoom (supporting both keyCode and e.key checks)

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
src/simulator/src/listeners.js Replaced MouseScroll (zoom) with handleCanvasPan (pan), removed jQuery zoom slider listeners, added setZoomFromSlider/getZoomSliderValue exports, removed desktop app Tauri event listeners
src/simulator/src/engine.js Updated comment to clarify existing drag-to-pan behavior
src/simulator/src/embedListeners.js Enhanced keyboard zoom shortcuts to support e.key in addition to keyCode
src/components/Navbar/QuickButton/QuickButton.vue Replaced jQuery zoom slider with Vue reactive implementation, added zoom conversion logic and state management
src/components/Navbar/QuickButton/QuickButtonMobile.vue Replaced jQuery zoom slider with Vue reactive implementation (similar to desktop but with mobile-specific styling)

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/components/Navbar/QuickButton/QuickButton.vue Outdated
Comment thread src/components/Navbar/QuickButton/QuickButton.vue Outdated
Comment thread src/components/Navbar/QuickButton/QuickButtonMobile.vue Outdated
Comment thread src/simulator/src/listeners.js
Comment thread src/simulator/src/listeners.js Outdated
Comment thread src/simulator/src/embedListeners.js Outdated
Comment thread src/simulator/src/listeners.js Outdated
Comment thread src/simulator/src/listeners.js Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (3)
src/simulator/src/listeners.js (2)

781-783: Refresh this comment to match current behavior.

The note says slider-based zoom was removed, but this PR adds slider helpers and slider-driven zoom usage. Keeping this stale will mislead future changes.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/simulator/src/listeners.js` around lines 781 - 783, Update the stale
comment near the embed check in listeners.js: replace the note that says the
slider was removed and zoom is controlled only by ZoomIn/ZoomOut with a concise
description that the code now includes slider helpers and supports slider-driven
zoom (mentioning the zoomSliderListeners helper and the ZoomIn/ZoomOut
controls), and clarify when the slider logic is enabled/disabled relative to the
embed flag so future readers won't be misled.

600-620: Add deltaMode normalization to ensure consistent pan speeds across browsers and devices.

The extractScrollDelta() function currently extracts deltaX and deltaY without checking the deltaMode property. When deltaMode is 1 (lines) or 2 (pages), these values need pixel normalization. Without this, pan speed will be inconsistent across different browsers and input devices.

Proposed fix
 function extractScrollDelta(event) {
     let deltaX = 0
     let deltaY = 0
@@
     if (event.deltaX !== undefined && event.deltaY !== undefined) {
         // Modern browsers: event.deltaX, event.deltaY
         deltaX = event.deltaX
         deltaY = event.deltaY
+        // Normalize to pixel units when needed
+        if (event.deltaMode === 1) {
+            // lines -> pixels (approx)
+            deltaX *= 16
+            deltaY *= 16
+        } else if (event.deltaMode === 2) {
+            // pages -> pixels (approx viewport)
+            deltaX *= window.innerWidth
+            deltaY *= window.innerHeight
+        }
     } else if (event.wheelDeltaX !== undefined && event.wheelDeltaY !== undefined) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/simulator/src/listeners.js` around lines 600 - 620, The
extractScrollDelta function must normalize for event.deltaMode so pan speeds are
consistent: after computing deltaX/deltaY from deltaX/deltaY, wheelDelta*,
wheelDelta or detail, check event.deltaMode and, when ===1 (lines) multiply
deltas by a reasonable line height constant (e.g., ~16) and when ===2 (pages)
multiply by viewport height (e.g., window.innerHeight) to convert to pixels;
ensure this normalization uses the already-computed deltaX/deltaY values and is
applied before returning from extractScrollDelta.
src/components/Navbar/QuickButton/QuickButton.vue (1)

99-153: Extract shared zoom mapping logic into a composable.

This conversion/clamp/init/update block is duplicated with src/components/Navbar/QuickButton/QuickButtonMobile.vue, which increases drift risk.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/Navbar/QuickButton/QuickButton.vue` around lines 99 - 153, The
zoom conversion/clamping/initialization/update logic is duplicated and should be
extracted into a reusable composable; create a composable (e.g.,
useZoomComposable) that exports the constants DISPLAY_ZOOM_MIN/MAX/DEFAULT/STEP
and INTERNAL_ZOOM_MIN/MAX, the reactive displayZoomPercent ref, and the
functions clampZoomPercent, convertInternalToDisplayZoom,
convertDisplayToInternalZoom, initializeZoomSlider and updateSimulatorZoom
(which internally call getZoomSliderValue and setZoomFromSlider as before), then
replace the duplicated implementations in QuickButton.vue and
QuickButtonMobile.vue by importing and using the composable so both components
share the same logic and behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/components/Navbar/QuickButton/QuickButton.vue`:
- Around line 121-156: The zoom slider is only synced once in onMounted via
initializeZoomSlider, so displayZoomPercent can drift when zoom changes
elsewhere; fix by adding a reactive sync that updates displayZoomPercent
whenever the authoritative zoom changes (e.g., add a watcher or event listener
that calls initializeZoomSlider or updateSimulatorZoom when the simulator's
zoom/state changes), reference initializeZoomSlider, updateSimulatorZoom and
onMounted to locate the logic, and ensure you register the listener/watcher
during mount and remove it onBeforeUnmount to avoid leaks.

In `@src/simulator/src/embedListeners.js`:
- Around line 182-187: The keyboard shortcut checks for zoom use incorrect
`KeyboardEvent` property casing—replace the undefined `e.KeyCode` and
`e.Keycode` with the correct `e.keyCode` in the zoom-in and zoom-out
conditionals so the control+plus and control+minus shortcuts properly detect key
codes; update the conditions around the ZoomIn() and ZoomOut() calls in
embedListeners (the lines checking `simulationArea.controlDown`) to use
`e.keyCode` consistently.

In `@src/simulator/src/listeners.js`:
- Around line 659-665: The legacy-only listeners for canvas panning miss modern
browsers; add a standard "wheel" event listener on the element with id
"simulationArea" so handleCanvasPan is invoked for modern wheel events (it
already supports deltaX/deltaY via extractScrollDelta); update the registration
near the existing calls that addEventListener('mousewheel', handleCanvasPan) and
addEventListener('DOMMouseScroll', handleCanvasPan) to also call
addEventListener('wheel', handleCanvasPan) so panning works reliably across
browsers.

---

Nitpick comments:
In `@src/components/Navbar/QuickButton/QuickButton.vue`:
- Around line 99-153: The zoom conversion/clamping/initialization/update logic
is duplicated and should be extracted into a reusable composable; create a
composable (e.g., useZoomComposable) that exports the constants
DISPLAY_ZOOM_MIN/MAX/DEFAULT/STEP and INTERNAL_ZOOM_MIN/MAX, the reactive
displayZoomPercent ref, and the functions clampZoomPercent,
convertInternalToDisplayZoom, convertDisplayToInternalZoom, initializeZoomSlider
and updateSimulatorZoom (which internally call getZoomSliderValue and
setZoomFromSlider as before), then replace the duplicated implementations in
QuickButton.vue and QuickButtonMobile.vue by importing and using the composable
so both components share the same logic and behavior.

In `@src/simulator/src/listeners.js`:
- Around line 781-783: Update the stale comment near the embed check in
listeners.js: replace the note that says the slider was removed and zoom is
controlled only by ZoomIn/ZoomOut with a concise description that the code now
includes slider helpers and supports slider-driven zoom (mentioning the
zoomSliderListeners helper and the ZoomIn/ZoomOut controls), and clarify when
the slider logic is enabled/disabled relative to the embed flag so future
readers won't be misled.
- Around line 600-620: The extractScrollDelta function must normalize for
event.deltaMode so pan speeds are consistent: after computing deltaX/deltaY from
deltaX/deltaY, wheelDelta*, wheelDelta or detail, check event.deltaMode and,
when ===1 (lines) multiply deltas by a reasonable line height constant (e.g.,
~16) and when ===2 (pages) multiply by viewport height (e.g.,
window.innerHeight) to convert to pixels; ensure this normalization uses the
already-computed deltaX/deltaY values and is applied before returning from
extractScrollDelta.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5adad52 and fd14070.

📒 Files selected for processing (5)
  • src/components/Navbar/QuickButton/QuickButton.vue
  • src/components/Navbar/QuickButton/QuickButtonMobile.vue
  • src/simulator/src/embedListeners.js
  • src/simulator/src/engine.js
  • src/simulator/src/listeners.js

Comment thread src/components/Navbar/QuickButton/QuickButton.vue Outdated
Comment thread src/simulator/src/embedListeners.js Outdated
Comment thread src/simulator/src/listeners.js Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
src/simulator/src/listeners.js (1)

663-669: ⚠️ Potential issue | 🟠 Major

Register the standard wheel listener for reliable panning.

Line 665-Line 669 only bind legacy events. On browsers that emit only wheel, canvas pan won’t trigger.

Proposed fix
-    document
-        .getElementById('simulationArea')
-        .addEventListener('mousewheel', handleCanvasPan)
-    document
-        .getElementById('simulationArea')
-        .addEventListener('DOMMouseScroll', handleCanvasPan)
+    const simulationAreaElement = document.getElementById('simulationArea')
+    simulationAreaElement.addEventListener('wheel', handleCanvasPan, { passive: false })
+    simulationAreaElement.addEventListener('mousewheel', handleCanvasPan, { passive: false })
+    simulationAreaElement.addEventListener('DOMMouseScroll', handleCanvasPan, { passive: false })
#!/bin/bash
# Verify which wheel-related listeners are currently registered in main simulator listeners.
rg -n "addEventListener\\(['\"](wheel|mousewheel|DOMMouseScroll)['\"]" src/simulator/src/listeners.js -C2
🧹 Nitpick comments (4)
src/components/Navbar/QuickButton/QuickButton.vue (2)

75-76: Set explicit type="button" on the new zoom controls.

Without explicit type, these default to submit in form contexts.

Proposed fix
-            <button class="zoom-button-decrement" `@click`="decrement" title="Zoom Out">−</button>
-            <button class="zoom-button-increment" `@click`="increment" title="Zoom In">+</button>
+            <button type="button" class="zoom-button-decrement" `@click`="decrement" title="Zoom Out">−</button>
+            <button type="button" class="zoom-button-increment" `@click`="increment" title="Zoom In">+</button>

223-269: Remove stale slider-specific styles to keep CSS aligned with the template.

The slider UI is no longer present, but .zoom-slider / .zoom-label rules remain.

src/components/Navbar/QuickButton/QuickButtonMobile.vue (2)

86-87: Set explicit type="button" for mobile zoom buttons.

This avoids accidental form submission behavior in embedded/form contexts.

Proposed fix
-          <button class="zoom-button-decrement" `@click`="decrement" title="Zoom Out">−</button>
-          <button class="zoom-button-increment" `@click`="increment" title="Zoom In">+</button>
+          <button type="button" class="zoom-button-decrement" `@click`="decrement" title="Zoom Out">−</button>
+          <button type="button" class="zoom-button-increment" `@click`="increment" title="Zoom In">+</button>

176-222: Clean up leftover slider CSS in mobile quick controls.

.zoom-slider and .zoom-label styles are no longer used after the button-only zoom UI switch.


ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fd14070 and 6522940.

📒 Files selected for processing (4)
  • src/components/Navbar/QuickButton/QuickButton.vue
  • src/components/Navbar/QuickButton/QuickButtonMobile.vue
  • src/simulator/src/embedListeners.js
  • src/simulator/src/listeners.js

Comment thread src/simulator/src/listeners.js

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2


ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6522940 and 4ef87f0.

📒 Files selected for processing (1)
  • src/simulator/src/listeners.js

Comment thread src/simulator/src/listeners.js
Comment thread src/simulator/src/listeners.js

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4ef87f0 and c13a478.

📒 Files selected for processing (1)
  • src/simulator/src/listeners.js

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2


ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c13a478 and 929641c.

📒 Files selected for processing (1)
  • src/simulator/src/listeners.js

Comment thread src/simulator/src/listeners.js
Comment thread src/simulator/src/listeners.js
@AkshitBitsian356u AkshitBitsian356u force-pushed the main branch 2 times, most recently from c13a478 to 4ef87f0 Compare March 1, 2026 17:14
@naman79820

Copy link
Copy Markdown
Member

Please follow pr template :)) @AkshitBitsian356u

@aryanndwi123 aryanndwi123 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @AkshitBitsian356u !!! Good direction on trackpad-to-pan! A few blockers:

  • All Tauri listen() desktop handlers are accidentally deleted in listeners.js
  • const DPR must move after imports.
  • MIN_ZOOM_SCALE should be 0.5 * DPR.
    Fix the listen() deletion first, rest are minor!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature : Canvas panning feels unnatural - trackpad gestures and zoom/pan interaction needs improvement

4 participants