Skip to content

refactor: remove jQuery from listeners.js#771

Open
pantha704 wants to merge 6 commits intoCircuitVerse:mainfrom
pantha704:jquery-to-vue-listeners#433
Open

refactor: remove jQuery from listeners.js#771
pantha704 wants to merge 6 commits intoCircuitVerse:mainfrom
pantha704:jquery-to-vue-listeners#433

Conversation

@pantha704
Copy link
Contributor

@pantha704 pantha704 commented Jan 11, 2026

Fixes #433 (partial - listeners.js file)

Changes

Removed all jQuery from src/simulator/src/listeners.js and replaced with native DOM APIs.

jQuery → Native DOM Conversions

jQuery Pattern Native Replacement
$('.dropdown.open').removeClass() classList.remove()
$('#projectName').on('click') addEventListener('click')
$(document).on('keyup') document.addEventListener('keyup')
$(document).on('input change') addEventListener('input') + addEventListener('change')
$('#subcircuitMenu').on('dragstop') Native HTML5 dragend event
$('#el').mouseover/mouseout addEventListener('mouseover'/'mouseout')
$('#tabsBar div').each().css() querySelectorAll().forEach() + style
$('#customRange1').val().change() value + dispatchEvent(new Event())
$('#decrement/#increment').click() addEventListener('click')

Technical Details

  • Used event delegation for dynamic elements
  • Maintained jQuery's 'fast' animation timing (200ms) via CSS transitions
  • Added null checks for safer DOM access

Stats

  • 356 insertions, 278 deletions
  • 0 jQuery instances remaining in listeners.js

Testing

  • ✅ Build passes (bun run build)
  • ✅ All event listeners work correctly

Code Understanding and AI Usage

  • I have reviewed every single line of the changes
  • I can explain the purpose and logic of each DOM API replacement
  • I have tested the event handling behavior
  • I have modified the code to follow project coding standards

Summary by CodeRabbit

  • New Features

    • Added a native zoom slider, dedicated Zoom In/Out controls, and programmatic zoom commands.
  • Refactor

    • Replaced jQuery-based UI and event handling with native DOM interactions.
    • Migrated sub-element drag-and-drop and unified pan/zoom/selection flows to native behavior.
  • Bug Fixes

    • Stabilized keyboard shortcuts and simulation-area event handling.
    • Ensure a scheduled backup when opening combinational-analysis prompts and alert when no input variables exist.

✏️ Tip: You can customize this high-level summary in your review settings.

@netlify
Copy link

netlify bot commented Jan 11, 2026

Deploy Preview for circuitverse ready!

Name Link
🔨 Latest commit 0a96718
🔍 Latest deploy log https://app.netlify.com/projects/circuitverse/deploys/696760090575f90008fc61e0
😎 Deploy Preview https://deploy-preview-771--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: 43 (🟢 up 9 from production)
Accessibility: 73 (no change from production)
Best Practices: 92 (no change from production)
SEO: 82 (no change from production)
PWA: -
View the detailed breakdown and full score reports

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

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 11, 2026

Walkthrough

Reworks src/simulator/src/listeners.js to replace jQuery usage with native DOM APIs (querySelector/All, addEventListener, classList, dragend, etc.), rewriting event bindings for keyup, dblclick, resize, pan/pinch/zoom, and subcircuit drag handling. Adds native zoom controls including a zoom slider and exported functions ZoomIn() and ZoomOut() that call an internal handleZoom and trigger redraws. Also applies formatting and minor control-flow refinements. Net change: +344 additions / -280 deletions.

🚥 Pre-merge checks | ✅ 2 | ❌ 3
❌ Failed checks (3 warnings)
Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR removes jQuery from listeners.js and replaces it with native DOM APIs, directly addressing issue #433's objective to eliminate jQuery usage. However, the linked issue also expects DOM manipulation logic to be migrated to Vue components using Vue reactivity, which this PR does not accomplish. Consider whether this PR should also migrate imperative DOM manipulation logic into Vue reactive patterns, or clarify with maintainers if the jQuery removal is an acceptable intermediate step before Vue migration.
Out of Scope Changes check ⚠️ Warning The PR includes numerous formatting changes (semicolon removal, import reformatting, object spacing, indentation) that are unrelated to the jQuery removal objective, as noted in code review feedback from coderabbitai. Revert non-essential formatting changes and preserve original code style; apply only jQuery-to-native conversions and safety-related changes (null checks) to keep the PR focused on its stated objective.
Docstring Coverage ⚠️ Warning Docstring coverage is 40.91% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically summarizes the main change: removing jQuery from listeners.js and replacing it with native DOM APIs.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

🧹 Recent nitpick comments
v1/src/simulator/src/listeners.js (1)

803-810: Remove unused parameter e.

The parameter e is defined but never used in the input event handler, as flagged by static analysis.

♻️ Proposed fix
-        customRange1.addEventListener('input', function (e) {
+        customRange1.addEventListener('input', function () {
src/simulator/src/listeners.js (1)

803-810: Remove unused parameter e.

Same issue as in v1/src/simulator/src/listeners.js. Flagged by static analysis.

♻️ Proposed fix
-        customRange1.addEventListener('input', function (e) {
+        customRange1.addEventListener('input', function () {

📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c74924f and 0a96718.

📒 Files selected for processing (2)
  • src/simulator/src/listeners.js
  • v1/src/simulator/src/listeners.js
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: pantha704
Repo: CircuitVerse/cv-frontend-vue PR: 0
File: :0-0
Timestamp: 2026-01-12T04:23:43.985Z
Learning: Issue `#433` requires jQuery removal to be submitted as one file per PR, keeping each pull request focused on a single file's migration from jQuery to Vue/native DOM APIs.
📚 Learning: 2026-01-13T21:31:27.969Z
Learnt from: naman79820
Repo: CircuitVerse/cv-frontend-vue PR: 791
File: src/simulator/src/setup.js:175-187
Timestamp: 2026-01-13T21:31:27.969Z
Learning: In src/simulator/src/setup.js, startMainListeners() is intentionally NOT called directly in setup(). Instead, it's initialized via Vue lifecycle through setupPanelsWhenReady() (called from Extra.vue onMounted), which waits for all panels to be mounted before attaching listeners. This lifecycle-driven approach prevents race conditions on first Tauri launch.

Applied to files:

  • v1/src/simulator/src/listeners.js
  • src/simulator/src/listeners.js
🧬 Code graph analysis (1)
v1/src/simulator/src/listeners.js (5)
src/simulator/src/engine.js (1)
  • updateCanvasSet (114-116)
src/simulator/src/restrictedElementDiv.js (3)
  • globalScope (4-4)
  • showRestricted (35-40)
  • hideRestricted (42-44)
src/simulator/src/canvasApi.js (1)
  • findDimensions (10-58)
src/simulator/src/ux.js (2)
  • exitFullView (405-424)
  • element (484-484)
src/simulator/src/data.js (1)
  • logixFunction (24-24)
🔇 Additional comments (8)
v1/src/simulator/src/listeners.js (5)

337-346: LGTM! Null check for projname element added.

The null check on line 343 properly guards against runtime errors when the element doesn't exist.


745-755: LGTM! Proper null checks for restricted element listeners.

The null check pattern with if (el) before attaching event listeners is correct.


763-771: LGTM! Native DOM resize handler.

Proper use of document.body.offsetWidth, querySelector, and null check for sidebar element.


786-791: LGTM! Public ZoomIn and ZoomOut exports.

Clean delegation to internal handleZoom function.


857-943: LGTM! Tauri event listeners are properly wired.

Desktop app listeners are correctly set up using @tauri-apps/api/event.

src/simulator/src/listeners.js (3)

337-346: LGTM! Null check for projname element added.

Proper guard against runtime errors.


786-791: LGTM! Public ZoomIn and ZoomOut exports.

Clean delegation to internal handleZoom function.


857-943: LGTM! Tauri event listeners are properly wired.

Desktop app listeners correctly use @tauri-apps/api/event.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.


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.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/simulator/src/listeners.js (2)

332-383: Add null-guards (or early return) around #simulationArea in startListeners().
You added some null checks (e.g., projectNameEl), but other calls assume document.getElementById('simulationArea') always exists; if it doesn’t (embed/SSR/test), this throws during listener setup.

Proposed fix (pattern)
+    const simulationAreaEl = document.getElementById('simulationArea')
+    if (!simulationAreaEl) return
+
     document
-        .getElementById('simulationArea')
+        .getElementById('simulationArea')
         .addEventListener('mouseup', (e) => {

(Then reuse simulationAreaEl for the other listeners in this function.)


610-631: Deduplicate MouseScroll() and prefer wheel over deprecated scroll events.
deltaY is computed twice and preventDefault() is called twice; also mousewheel/DOMMouseScroll are legacy and may behave inconsistently.

Proposed fix
     function MouseScroll(event) {
         updateCanvasSet(true)
         event.preventDefault()
-        var deltaY = event.wheelDelta ? event.wheelDelta : -event.detail
-        event.preventDefault()
-        var deltaY = event.wheelDelta ? event.wheelDelta : -event.detail
+        const deltaY = event.wheelDelta ? event.wheelDelta : -event.detail
         const direction = deltaY > 0 ? 1 : -1
         handleZoom(direction)
@@
     document
         .getElementById('simulationArea')
-        .addEventListener('mousewheel', MouseScroll)
-    document
-        .getElementById('simulationArea')
-        .addEventListener('DOMMouseScroll', MouseScroll)
+        .addEventListener('wheel', MouseScroll, { passive: false })
🤖 Fix all issues with AI agents
In @src/simulator/src/listeners.js:
- Around line 320-329: The expression setting simulationArea.mouseX uses wrong
operator precedence: "100 + simulationArea.maxWidth || 0" yields NaN when
maxWidth is undefined; change it to add a safe default before the || (or better,
use nullish coalescing). For example update the assignment inside the touch
branch (near toRefs(simulatorMobileStore), findDimensions and getTap calls) to:
simulationArea.mouseX = 100 + (simulationArea.maxWidth ?? 0) so 100 is preserved
when maxWidth is undefined.
- Around line 715-758: The current drag implementation relies only on the
'dragend' handler and getBoundingClientRect() (in the listener attached to
subcircuitMenu), which doesn't provide the true drop coordinates and also
mutates the original object in globalScope; fix it by: ensure draggable
subcircuit DOM nodes (class 'draggableSubcircuitElement') have draggable="true"
and set dataTransfer in a 'dragstart' handler (e.g. store elementName and
elementId and optional offset), add a 'dragover' handler on the drop container
(use guide_1/sideBarWidth logic) that calls event.preventDefault() to allow
drops, and implement a 'drop' handler that reads event.dataTransfer to identify
the element, computes drop coords from event.clientX/Y minus the sidebar width
(instead of getBoundingClientRect()), then create a new copy of the source
object (do not reuse globalScope[elementName][elementId] directly) — set its x/y
and each node.x/node.y appropriately, push the new object into
tempBuffer.subElements, and remove the dragged DOM node from the menu;
update/remove the existing 'dragend' logic to avoid mutating the original
tempElement.
🧹 Nitpick comments (3)
src/simulator/src/listeners.js (3)

778-786: resizeTabs() is fine, but consider guarding #tabsBar if it can be absent.
querySelectorAll('#tabsBar div') will just no-op if missing, so this is optional.


791-806: handleZoom() conditions are redundant (both branches do the same call).
This isn’t wrong, just extra complexity.

Proposed simplification
 function handleZoom(direction) {
-    if (globalScope.scale > 0.5 * DPR) {
-        changeScale(direction * 0.1 * DPR)
-    } else if (globalScope.scale < 4 * DPR) {
-        changeScale(direction * 0.1 * DPR)
-    }
+    changeScale(direction * 0.1 * DPR)
     gridUpdateSet(true)
     scheduleUpdate()
 }

871-955: Store and call the unlisten function returned by listen() for proper cleanup.

Tauri's event.listen() (v2.3.0) returns a Promise<UnlistenFn>. These calls ignore that return value, preventing cleanup. While setup() runs once at app initialization (limiting practical impact in a desktop context), not capturing these unlisteners violates the API contract and creates a pattern vulnerability if the setup code is ever called multiple times or if listeners are registered dynamically. Await each listen() call, store the returned function, and call it when the listener's context is no longer needed (e.g., on app unload or if setup is refactored to support teardown).

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 10d2489 and 4140ebe.

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

760-770: Restricted hover listeners look good (simple + guarded).
The per-element getElementById null-check keeps this safe across DOM variants.

Comment on lines 320 to 329
if (simulationArea.touch) {
const { isCopy } = toRefs(simulatorMobileStore)
// small hack so Current circuit element should not spwan above last circuit element
if (!isCopy.value) {
findDimensions(globalScope);
simulationArea.mouseX = 100 + simulationArea.maxWidth || 0;
simulationArea.mouseY = simulationArea.minHeight || 0;
getTap(e);
findDimensions(globalScope)
simulationArea.mouseX = 100 + simulationArea.maxWidth || 0
simulationArea.mouseY = simulationArea.minHeight || 0
getTap(e)
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Fix operator precedence in simulationArea.mouseX = 100 + simulationArea.maxWidth || 0.
If simulationArea.maxWidth is undefined, 100 + undefined becomes NaN, and the || 0 turns the result into 0 (not 100).

Proposed fix
-            simulationArea.mouseX = 100 + simulationArea.maxWidth || 0
+            simulationArea.mouseX = 100 + (simulationArea.maxWidth || 0)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (simulationArea.touch) {
const { isCopy } = toRefs(simulatorMobileStore)
// small hack so Current circuit element should not spwan above last circuit element
if (!isCopy.value) {
findDimensions(globalScope);
simulationArea.mouseX = 100 + simulationArea.maxWidth || 0;
simulationArea.mouseY = simulationArea.minHeight || 0;
getTap(e);
findDimensions(globalScope)
simulationArea.mouseX = 100 + simulationArea.maxWidth || 0
simulationArea.mouseY = simulationArea.minHeight || 0
getTap(e)
}
}
if (simulationArea.touch) {
const { isCopy } = toRefs(simulatorMobileStore)
// small hack so Current circuit element should not spwan above last circuit element
if (!isCopy.value) {
findDimensions(globalScope)
simulationArea.mouseX = 100 + (simulationArea.maxWidth || 0)
simulationArea.mouseY = simulationArea.minHeight || 0
getTap(e)
}
}
🤖 Prompt for AI Agents
In @src/simulator/src/listeners.js around lines 320 - 329, The expression
setting simulationArea.mouseX uses wrong operator precedence: "100 +
simulationArea.maxWidth || 0" yields NaN when maxWidth is undefined; change it
to add a safe default before the || (or better, use nullish coalescing). For
example update the assignment inside the touch branch (near
toRefs(simulatorMobileStore), findDimensions and getTap calls) to:
simulationArea.mouseX = 100 + (simulationArea.maxWidth ?? 0) so 100 is preserved
when maxWidth is undefined.

Comment on lines 715 to +758
// 'drag and drop' event listener for subcircuit elements in layout mode
$('#subcircuitMenu').on('dragstop', '.draggableSubcircuitElement', function (event, ui) {
const sideBarWidth = $('#guide_1')[0].clientWidth;
let tempElement;

if (ui.position.top > 10 && ui.position.left > sideBarWidth) {
// Make a shallow copy of the element with the new coordinates
tempElement = globalScope[this.dataset.elementName][this.dataset.elementId];
// Changing the coordinate doesn't work yet, nodes get far from element
tempElement.x = ui.position.left - sideBarWidth;
tempElement.y = ui.position.top;
for (const node of tempElement.nodeList) {
node.x = ui.position.left - sideBarWidth;
node.y = ui.position.top;
// Using native HTML5 drag events instead of jQuery UI
const subcircuitMenu = document.getElementById('subcircuitMenu')
if (subcircuitMenu) {
// Use event delegation for dragend events on draggable subcircuit elements
subcircuitMenu.addEventListener('dragend', function (event) {
const target = event.target
if (
!(target instanceof HTMLElement) ||
!target.classList.contains('draggableSubcircuitElement')
) {
return
}

tempBuffer.subElements.push(tempElement);
this.parentElement.removeChild(this);
}
});
const guide1 = document.getElementById('guide_1')
const sideBarWidth = guide1 ? guide1.clientWidth : 0

// Get the final position from the drag event
const rect = target.getBoundingClientRect()
const posTop = rect.top
const posLeft = rect.left

if (posTop > 10 && posLeft > sideBarWidth) {
// Make a shallow copy of the element with the new coordinates
const elementName = target.dataset.elementName
const elementId = target.dataset.elementId
if (elementName && elementId) {
const tempElement = globalScope[elementName][elementId]
if (tempElement) {
// Changing the coordinate doesn't work yet, nodes get far from element
tempElement.x = posLeft - sideBarWidth
tempElement.y = posTop
for (const node of tempElement.nodeList) {
node.x = posLeft - sideBarWidth
node.y = posTop
}

restrictedElements.forEach((element) => {
$(`#${element}`).mouseover(() => {
showRestricted()
tempBuffer.subElements.push(tempElement)
target.parentElement?.removeChild(target)
}
}
}
})
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify how subcircuit elements are marked draggable, and whether any drag/dragover/drop handlers exist
rg -n --hidden -S "draggableSubcircuitElement|subcircuitMenu|addEventListener\\('(drag|drop|dragover|dragend)'" -C 3
# Find how tempBuffer.subElements is later consumed (does it expect new instances?)
rg -n --hidden -S "tempBuffer\\.subElements|subElements\\.push" -C 3

Repository: CircuitVerse/cv-frontend-vue

Length of output: 6055


🏁 Script executed:

#!/bin/bash
# Search for draggable attribute and any drag/drop handler setup
rg -n --hidden -S "draggable|dragstart|dragover|drop" -C 2
# Also check for any element cloning/copying logic
rg -n --hidden -S "cloneNode|Object\\.assign|spread.*tempElement|\\.slice\\(\\)|JSON\\.parse" -C 2

Repository: CircuitVerse/cv-frontend-vue

Length of output: 50385


🏁 Script executed:

#!/bin/bash
# Search for draggable attribute in HTML or element setup
rg -n "draggable\s*=" src/simulator/src/listeners.js -C 2

# Check for drop or dragover handlers in current listeners.js
rg -n "dragover|drop" src/simulator/src/listeners.js -C 2

# Check if cloneNode or any deep copy is used
rg -n "cloneNode|Object\.assign|JSON\.stringify" src/simulator/src/listeners.js

Repository: CircuitVerse/cv-frontend-vue

Length of output: 462


🏁 Script executed:

#!/bin/bash
# Search for where elements are marked as draggable (could be in HTML or CSS)
rg -n "draggable" src/simulator/src/ --type html -C 2

# Check if draggable attribute is set programmatically anywhere
rg -n "setAttribute.*draggable|draggable.*true" src/simulator/src/listeners.js

# Search entire listeners.js for complete drag-drop flow
rg -n "subcircuitMenu|draggableSubcircuitElement" src/simulator/src/listeners.js -C 4

Repository: CircuitVerse/cv-frontend-vue

Length of output: 764


HTML5 drag-drop implementation is incomplete and will not function correctly.

The code lacks critical handlers for standard HTML5 drag-drop:

  • No dragover event handler to define drop zones
  • No drop event handler to capture the actual drop coordinates
  • No visible draggable="true" attribute setup on the elements

Without these handlers, dragend + getBoundingClientRect() cannot determine where the user intended to drop. getBoundingClientRect() returns the element's current DOM position (unchanged by dragging), not the user's target location. The previous jQuery UI code relied on ui.position, which provided this information; the native HTML5 replacement must implement dragover + drop handlers instead.

Additionally, tempElement is taken by reference from globalScope[elementName][elementId], mutated directly, and pushed into tempBuffer.subElements. This aliases the original object rather than creating a new instance, causing unintended side effects.

🤖 Prompt for AI Agents
In @src/simulator/src/listeners.js around lines 715 - 758, The current drag
implementation relies only on the 'dragend' handler and getBoundingClientRect()
(in the listener attached to subcircuitMenu), which doesn't provide the true
drop coordinates and also mutates the original object in globalScope; fix it by:
ensure draggable subcircuit DOM nodes (class 'draggableSubcircuitElement') have
draggable="true" and set dataTransfer in a 'dragstart' handler (e.g. store
elementName and elementId and optional offset), add a 'dragover' handler on the
drop container (use guide_1/sideBarWidth logic) that calls
event.preventDefault() to allow drops, and implement a 'drop' handler that reads
event.dataTransfer to identify the element, computes drop coords from
event.clientX/Y minus the sidebar width (instead of getBoundingClientRect()),
then create a new copy of the source object (do not reuse
globalScope[elementName][elementId] directly) — set its x/y and each
node.x/node.y appropriately, push the new object into tempBuffer.subElements,
and remove the dragged DOM node from the menu; update/remove the existing
'dragend' logic to avoid mutating the original tempElement.

@pantha704
Copy link
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 12, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@pantha704 pantha704 force-pushed the jquery-to-vue-listeners#433 branch from b74b0df to ea5edc6 Compare January 12, 2026 04:09
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/simulator/src/listeners.js (1)

609-622: Remove duplicate code in MouseScroll function.

Lines 612-614 contain duplicate code: deltaY is declared twice and event.preventDefault() is called twice. This appears to be a copy-paste oversight.

🐛 Proposed fix
     function MouseScroll(event) {
         updateCanvasSet(true)
         event.preventDefault()
         var deltaY = event.wheelDelta ? event.wheelDelta : -event.detail
-        event.preventDefault()
-        var deltaY = event.wheelDelta ? event.wheelDelta : -event.detail
         const direction = deltaY > 0 ? 1 : -1
         handleZoom(direction)
         updateCanvasSet(true)
         gridUpdateSet(true)

         if (layoutModeGet()) layoutUpdate()
         else update() // Schedule update not working, this is INEFFICIENT
     }
🧹 Nitpick comments (2)
src/simulator/src/listeners.js (2)

89-91: Consider removing the empty else block.

The empty else block with only a comment adds no value and can be removed for cleaner code.

♻️ Suggested cleanup
     if (tapLength < 500 && tapLength > 0) {
         onDoubleClickorTap(e)
-    } else {
-        // Single tap
     }

806-866: Add null checks for DOM element access.

The zoomSliderListeners function accesses multiple DOM elements without null checks, which could throw errors if elements don't exist (e.g., in embed mode). This is inconsistent with the defensive pattern used for projectNameEl (lines 337-344).

♻️ Suggested defensive coding
 function zoomSliderListeners() {
-    document.getElementById('customRange1').value = 5
+    const customRange1 = document.getElementById('customRange1')
+    if (!customRange1) return
+    
+    customRange1.value = 5
     document
         .getElementById('simulationArea')
         .addEventListener('DOMMouseScroll', zoomSliderScroll)
     document
         .getElementById('simulationArea')
         .addEventListener('mousewheel', zoomSliderScroll)
     let curLevel = document.getElementById('customRange1').value
-    const customRange1 = document.getElementById('customRange1')
     customRange1.addEventListener('input', function (e) {
         // ... existing code
     })
     // ... rest of function
     
-    document.getElementById('decrement').addEventListener('click', () => {
-        sliderZoomButton(-1)
-    })
-    document.getElementById('increment').addEventListener('click', () => {
-        sliderZoomButton(1)
-    })
+    const decrementBtn = document.getElementById('decrement')
+    const incrementBtn = document.getElementById('increment')
+    decrementBtn?.addEventListener('click', () => {
+        sliderZoomButton(-1)
+    })
+    incrementBtn?.addEventListener('click', () => {
+        sliderZoomButton(1)
+    })
 }
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b74b0df and ea5edc6.

📒 Files selected for processing (1)
  • src/simulator/src/listeners.js
🔇 Additional comments (5)
src/simulator/src/listeners.js (5)

10-10: LGTM!

The consolidated import statements improve readability and maintainability.

Also applies to: 25-31


212-214: LGTM!

Good use of native querySelectorAll().forEach() to replace jQuery's class manipulation for closing open dropdowns.


719-756: Verify dragend position detection works correctly.

The native HTML5 dragend event behaves differently from jQuery UI's dragstop. In HTML5 drag/drop, getBoundingClientRect() on the target after dragend may return the element's original position (since the browser typically snaps the dragged element back), not the drop location.

For proper drop position detection, consider using the drop event on a drop zone or capturing clientX/clientY from the dragend event directly:

const posLeft = event.clientX
const posTop = event.clientY

Please verify this interaction works correctly during manual testing.


800-805: LGTM!

The new ZoomIn() and ZoomOut() exports provide a clean public API that delegates to the internal handleZoom function.


759-769: LGTM!

Good use of addEventListener for mouseover/mouseout events with proper null checks on each element.

.addEventListener('mousewheel', zoomSliderScroll)
let curLevel = document.getElementById('customRange1').value
const customRange1 = document.getElementById('customRange1')
customRange1.addEventListener('input', function (e) {

Check failure

Code scanning / ESLint

Disallow unused variables

'e' is defined but never used.
gridUpdateSet(true)
curLevel = newValue
})
customRange1.addEventListener('change', function (e) {

Check failure

Code scanning / ESLint

Disallow unused variables

'e' is defined but never used.
@tachyons
Copy link
Member

When making bug fixes, migration or any non feature changes, please make the some change to v1/src in the same PR

Fixes CircuitVerse#433

- Replaced $(.${outputListNames[i]}) with document.querySelectorAll()
- Native DOM API returns NodeList which works the same as jQuery collection
- Changed booleanInputVariables == 0 to booleanInputVariables.length === 0
- This was a pre-existing bug where comparing an array to 0 always returns false
- Now properly checks if the array is empty
@pantha704
Copy link
Contributor Author

Got it!

@pantha704 pantha704 force-pushed the jquery-to-vue-listeners#433 branch from 374ca1e to fd1adbb Compare January 13, 2026 07:23
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🤖 Fix all issues with AI agents
In @v1/src/simulator/src/listeners.js:
- Around line 336-344: The click handler for projectNameEl calls
document.getElementById('projname').select() without verifying the element
exists, which can throw; modify the arrow function inside the
projectNameEl.addEventListener to fetch the element into a variable (e.g., const
projInput = document.getElementById('projname')), check if projInput is non-null
before calling projInput.select(), and only call select() inside that
conditional while still setting simulationArea.lastSelected = globalScope.root
and preserving the setTimeout behavior.
- Around line 717-754: The dragend handler for the 'draggableSubcircuitElement'
uses target.getBoundingClientRect() which returns the element's original
position; replace this with the pointer coordinates from the event (use
event.clientX and event.clientY) or move logic to a proper drop handler on the
canvas; compute the element's canvas coordinates by subtracting the sidebar
width (guide_1.clientWidth) and any page scroll (window.scrollX/scrollY) from
event.clientX/event.clientY, then set tempElement.x/y and its node.x/y from
those computed values and push into tempBuffer.subElements (keep using the same
tempElement, tempBuffer, and dataset keys elementName/elementId).
- Around line 789-797: handleZoom currently uses two separate conditions that
both call changeScale, so zooming ignores direction and boundaries; update
handleZoom to check the zoom direction first and only call changeScale when the
new scale would be within bounds — e.g., if direction > 0 (zoom in) only call
changeScale when globalScope.scale < 4 * DPR, and if direction < 0 (zoom out)
only call changeScale when globalScope.scale > 0.5 * DPR; keep the existing
calls to gridUpdateSet(true) and scheduleUpdate() after a successful change (or
always, if desired), and use the existing symbols handleZoom, changeScale,
globalScope.scale, DPR, gridUpdateSet, scheduleUpdate to locate the code.
- Around line 804-864: The zoomSliderListeners function assumes DOM elements
exist (IDs customRange1, simulationArea, decrement, increment) and uses them
directly; add null checks and early returns where these are accessed: in
zoomSliderListeners validate document.getElementById('customRange1') and abort
if missing, guard adding 'DOMMouseScroll'/'mousewheel' listeners on
document.getElementById('simulationArea') only if present, and only attach click
listeners for 'decrement' and 'increment' when those elements exist; also ensure
zoomSliderScroll and sliderZoomButton handle null/invalid slider references
(parseInt only after existence) to avoid runtime exceptions.
- Around line 806-811: The code registers scroll listeners twice on the same
element causing duplicate handling; in particular the MouseScroll handler
(attached earlier) and the zoomSliderScroll handler (attached inside
zoomSliderListeners()) both run for wheel/DOMMouseScroll events. Fix by removing
the duplicate attachment in zoomSliderListeners() — either stop adding
mousewheel/DOMMouseScroll there or replace those calls with a single
consolidated handler that calls both MouseScroll and the slider UI update (or
have MouseScroll invoke the slider update). Ensure only one pair of
addEventListener calls target 'simulationArea' for wheel/DOMMouseScroll and
reference the existing MouseScroll and zoomSliderScroll functions (or a new
unified function) so events are processed once.
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ea5edc6 and fd1adbb.

📒 Files selected for processing (2)
  • src/simulator/src/listeners.js
  • v1/src/simulator/src/listeners.js
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: pantha704
Repo: CircuitVerse/cv-frontend-vue PR: 0
File: :0-0
Timestamp: 2026-01-12T04:23:43.985Z
Learning: Issue #433 requires jQuery removal to be submitted as one file per PR, keeping each pull request focused on a single file's migration from jQuery to Vue/native DOM APIs.
🔇 Additional comments (5)
v1/src/simulator/src/listeners.js (5)

10-31: LGTM!

Clean consolidation of imports using native DOM API utilities. The grouped imports from restrictedElementDiv improve readability.


64-94: LGTM!

Clean conversion of double-click/tap detection to native JavaScript. The timeout-based double-tap detection pattern is correctly implemented.


172-215: LGTM!

The panStart function is well-migrated. Good use of querySelectorAll().forEach() with classList.remove() at lines 212-214 to replace jQuery's class manipulation.


757-767: LGTM!

Good use of event delegation pattern with null checks before attaching mouseover/mouseout listeners.


868-954: LGTM!

Desktop app listeners using Tauri's listen API are clean and well-structured. Formatting improvements are consistent.

Comment on lines +806 to +811
document
.getElementById('simulationArea')
.addEventListener('DOMMouseScroll', zoomSliderScroll)
document
.getElementById('simulationArea')
.addEventListener('mousewheel', zoomSliderScroll)
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

wc -l v1/src/simulator/src/listeners.js

Repository: CircuitVerse/cv-frontend-vue

Length of output: 109


🏁 Script executed:

sed -n '600,630p' v1/src/simulator/src/listeners.js

Repository: CircuitVerse/cv-frontend-vue

Length of output: 971


🏁 Script executed:

sed -n '800,820p' v1/src/simulator/src/listeners.js

Repository: CircuitVerse/cv-frontend-vue

Length of output: 885


🏁 Script executed:

# Search for both handler function definitions
rg -n "function MouseScroll|function zoomSliderScroll" v1/src/simulator/src/listeners.js

Repository: CircuitVerse/cv-frontend-vue

Length of output: 148


🏁 Script executed:

sed -n '830,850p' v1/src/simulator/src/listeners.js

Repository: CircuitVerse/cv-frontend-vue

Length of output: 939


🏁 Script executed:

# Get more context around zoomSliderScroll to understand its full implementation
sed -n '830,860p' v1/src/simulator/src/listeners.js

Repository: CircuitVerse/cv-frontend-vue

Length of output: 1249


🏁 Script executed:

rg -n "zoomSliderListeners" v1/src/simulator/src/listeners.js

Repository: CircuitVerse/cv-frontend-vue

Length of output: 138


🏁 Script executed:

# Check the context around where these listeners are registered - are they in main init or inside functions?
sed -n '595,635p' v1/src/simulator/src/listeners.js | cat -n

Repository: CircuitVerse/cv-frontend-vue

Length of output: 1567


🏁 Script executed:

# Check the context of zoomSliderListeners function definition
sed -n '800,825p' v1/src/simulator/src/listeners.js | cat -n

Repository: CircuitVerse/cv-frontend-vue

Length of output: 1263


Remove duplicate scroll event listeners to prevent double-processing.

Scroll events (mousewheel and DOMMouseScroll) are registered twice on simulationArea:

  1. Lines 622–627 attach the MouseScroll handler
  2. Lines 806–811 (within zoomSliderListeners()) attach the zoomSliderScroll handler

Both handlers execute on the same scroll events, causing redundant processing. The MouseScroll handler updates the zoom state and triggers canvas updates, while zoomSliderScroll updates the slider UI. This duplication can lead to synchronization issues and unnecessary handler invocations. Consider consolidating these into a single handler or clearly separating their responsibilities to avoid conflicts.

🤖 Prompt for AI Agents
In @v1/src/simulator/src/listeners.js around lines 806 - 811, The code registers
scroll listeners twice on the same element causing duplicate handling; in
particular the MouseScroll handler (attached earlier) and the zoomSliderScroll
handler (attached inside zoomSliderListeners()) both run for
wheel/DOMMouseScroll events. Fix by removing the duplicate attachment in
zoomSliderListeners() — either stop adding mousewheel/DOMMouseScroll there or
replace those calls with a single consolidated handler that calls both
MouseScroll and the slider UI update (or have MouseScroll invoke the slider
update). Ensure only one pair of addEventListener calls target 'simulationArea'
for wheel/DOMMouseScroll and reference the existing MouseScroll and
zoomSliderScroll functions (or a new unified function) so events are processed
once.

@pantha704
Copy link
Contributor Author

@coderabbitai full review

All previously reported issues have been addressed:

  • ✅ Math.sqrt distance calculation fixed
  • ✅ Yf uses globalScope.oy (not ox)
  • ✅ Duplicate code in MouseScroll removed
  • ✅ v1/src now has identical changes

Please perform a fresh full review of the current state.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 13, 2026

@pantha704, thank you for addressing all the previous feedback! I'll perform a fresh full review of the PR now that you've:

  • ✅ Fixed the Math.sqrt distance calculation
  • ✅ Corrected Yf to use globalScope.oy (not ox)
  • ✅ Removed duplicate code in MouseScroll
  • ✅ Applied identical changes to v1/src

I'll review the complete current state of both src/simulator/src/listeners.js and v1/src/simulator/src/listeners.js to ensure everything looks good.


🧠 Learnings used
Learnt from: pantha704
Repo: CircuitVerse/cv-frontend-vue PR: 0
File: :0-0
Timestamp: 2026-01-12T04:23:43.985Z
Learning: Issue #433 requires jQuery removal to be submitted as one file per PR, keeping each pull request focused on a single file's migration from jQuery to Vue/native DOM APIs.
✅ Actions performed

Full review triggered.

.addEventListener('mousewheel', zoomSliderScroll)
let curLevel = document.getElementById('customRange1').value
const customRange1 = document.getElementById('customRange1')
customRange1.addEventListener('input', function (e) {

Check failure

Code scanning / ESLint

Disallow unused variables

'e' is defined but never used.
gridUpdateSet(true)
curLevel = newValue
})
customRange1.addEventListener('change', function (e) {

Check failure

Code scanning / ESLint

Disallow unused variables

'e' is defined but never used.
@tachyons
Copy link
Member

Most of the changes on this PR are formatting changes which are unrelated to jquery, could you revert those changes and focus on jquery ?

cc @coderabbitai

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 13, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Fetch the diff for the PR to examine formatting vs functional changes
gh pr diff 771 --repo CircuitVerse/cv-frontend-vue > /tmp/pr771.diff

# Show the first 500 lines to understand the nature of changes
head -n 500 /tmp/pr771.diff

echo "---SEPARATOR---"

# Count lines that are just formatting (spaces, semicolons, etc.)
echo "Analyzing change patterns..."

# Look for semicolon-only changes
grep -E "^-.*;\s*$" /tmp/pr771.diff | head -n 20

echo "---SEMICOLON CHANGES---"

# Look for spacing-only changes
grep -E "^[-+]\s*(const|let|var|import|export).*" /tmp/pr771.diff | head -n 30

Length of output: 20175


@tachyons is correct - I can see from the diff that a significant portion of the changes are formatting-related rather than jQuery removal. Here are the main types of formatting changes that should be reverted:

Formatting changes to revert:

  1. Semicolon removal - Variables like let coordinate;let coordinate
  2. Import statement reformatting - Collapsing/expanding multi-line imports (lines 10-11, 25-30)
  3. Object literal spacing - Changes to indentation and spacing in objects like returnCoordinate
  4. Code indentation/alignment - Various spacing adjustments throughout

Actual jQuery changes to keep:

  • Line 213: $('.dropdown.open').removeClass('open')document.querySelectorAll('.dropdown.open').forEach((el) => el.classList.remove('open'))
  • Line 323: $(document).on('keyup', ...)document.addEventListener('keyup', ...)
  • Line 330: $('#projectName').on('click', ...)document.getElementById('projectName').addEventListener('click', ...)
  • Line 713: $('#subcircuitMenu').on('dragstop', ...) → Native dragend implementation

@pantha704, to clean this up:

  1. Create a fresh branch from the base branch
  2. Apply only jQuery-specific changes - replace jQuery selectors and methods with vanilla JS equivalents
  3. Keep the original formatting - don't run formatters, don't adjust spacing/semicolons/indentation unless absolutely necessary for the jQuery changes
  4. Test each change to ensure functionality is preserved

This will make the PR much easier to review and keep it focused on the stated objective of jQuery removal.


🧠 Learnings used
Learnt from: pantha704
Repo: CircuitVerse/cv-frontend-vue PR: 0
File: :0-0
Timestamp: 2026-01-12T04:23:43.985Z
Learning: Issue #433 requires jQuery removal to be submitted as one file per PR, keeping each pull request focused on a single file's migration from jQuery to Vue/native DOM APIs.

Learnt from: 092vk
Repo: CircuitVerse/cv-frontend-vue PR: 640
File: vite.config.ts:0-0
Timestamp: 2025-08-24T04:07:56.434Z
Learning: User 092vk prefers review comments to focus only on changes directly related to the main PR objective, even if other changes are present in the diff.

@pantha704
Copy link
Contributor Author

Yeah sure !

Replaced jQuery with native DOM methods:
- .removeClass() → document.querySelectorAll()
- .on('keyup') → document.addEventListener()
- .on('click') → getElementById().addEventListener()
- .on('dragstop') → native dragend event delegation
- .mouseover/mouseout → addEventListener('mouseover/mouseout')
- resizeTabs: replaced  with native DOM (querySelectorAll, offsetWidth, style)
- zoomSlider: replaced  with native getElementById and dispatchEvent
- increment/decrement: replaced .click with addEventListener

No formatting changes made. Applied to both src/ and v1/src/
@pantha704 pantha704 force-pushed the jquery-to-vue-listeners#433 branch from fd1adbb to c74924f Compare January 14, 2026 06:20
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@v1/src/simulator/src/listeners.js`:
- Around line 157-158: The Yf calculation is using the X offset (globalScope.ox)
by mistake; update the Yf formula to subtract the Y offset (globalScope.oy)
instead of globalScope.ox so it mirrors the Xf logic: compute Yf =
Math.round((RawY - globalScope.oy) / globalScope.scale / unit) while keeping
RawY, globalScope.scale and unit unchanged to fix pinch-zoom recentering; locate
and change the line that defines Yf in listeners.js where Xf and Yf are
computed.
- Around line 132-135: The pinch distance calculation incorrectly passes two
arguments to Math.sqrt (so the Y component is ignored); update the calculation
that assigns to distance in listeners.js to compute the Euclidean distance by
summing the squared X and Y differences and passing that single sum to Math.sqrt
(use e.touches[1]/[0] clientX and clientY values), ensuring the expression is
wrapped so Math.sqrt receives one numeric argument.
- Around line 323-327: The assignment to simulationArea.mouseX uses incorrect
operator precedence: "100 + simulationArea.maxWidth || 0" causes 100 + undefined
-> NaN then || 0 returns 0; change it so the default applies only to maxWidth
(e.g., compute 100 + (simulationArea.maxWidth || 0)). Update the expression in
the block guarded by isCopy.value (near findDimensions and getTap) to
parenthesize the maxWidth defaulting; leave mouseY's existing fallback for
minHeight as-is unless you also want the same explicit grouping.
♻️ Duplicate comments (10)
v1/src/simulator/src/listeners.js (4)

337-345: Add null check for projname element.

document.getElementById('projname').select() will throw if the element doesn't exist. This was flagged in a previous review.

🐛 Proposed fix
     projectNameEl.addEventListener('click', () => {
         simulationArea.lastSelected = globalScope.root
         setTimeout(() => {
-            document.getElementById('projname').select()
+            const projnameEl = document.getElementById('projname')
+            if (projnameEl) projnameEl.select()
         }, 100)
     })

776-784: handleZoom boundary checks are ineffective — both branches do the same thing.

The conditions scale > 0.5 * DPR and scale < 4 * DPR don't prevent zooming beyond limits based on direction. This was flagged in a previous review.

🐛 Proposed fix to enforce zoom boundaries
 function handleZoom(direction) {
-    if (globalScope.scale > 0.5 * DPR) {
-        changeScale(direction * 0.1 * DPR)
-    } else if (globalScope.scale < 4 * DPR) {
-        changeScale(direction * 0.1 * DPR)
-    }
+    const minScale = 0.5 * DPR
+    const maxScale = 4 * DPR
+    // Prevent zooming out below min or zooming in above max
+    if (direction < 0 && globalScope.scale <= minScale) return
+    if (direction > 0 && globalScope.scale >= maxScale) return
+    changeScale(direction * 0.1 * DPR)
     gridUpdateSet(true)
     scheduleUpdate()
 }

793-798: Duplicate scroll listeners cause double event processing.

mousewheel and DOMMouseScroll are registered twice on simulationArea:

  1. Lines 625-630 attach MouseScroll
  2. Lines 793-798 attach zoomSliderScroll

Both execute on every scroll. Consider consolidating into a single handler or having MouseScroll update the slider UI directly.


791-798: Null check needed before accessing customRange1 at line 792.

document.getElementById('customRange1').value = 5 at line 792 will throw if element doesn't exist, but the null check doesn't occur until line 801.

🐛 Proposed fix
 function zoomSliderListeners() {
-    document.getElementById('customRange1').value = 5
+    const customRange1 = document.getElementById('customRange1')
+    if (!customRange1) return
+    customRange1.value = 5
     document
         .getElementById('simulationArea')
         .addEventListener('DOMMouseScroll', zoomSliderScroll)
     document
         .getElementById('simulationArea')
         .addEventListener('mousewheel', zoomSliderScroll)
-    let curLevel = document.getElementById('customRange1').value
-    const customRange1 = document.getElementById('customRange1')
-    if (customRange1) {
+    let curLevel = customRange1.value
src/simulator/src/listeners.js (6)

337-345: Add null check for projname element.

Same issue as v1 counterpart. The select() call will throw if element doesn't exist.

🐛 Proposed fix
     projectNameEl.addEventListener('click', () => {
         simulationArea.lastSelected = globalScope.root
         setTimeout(() => {
-            document.getElementById('projname').select()
+            const projnameEl = document.getElementById('projname')
+            if (projnameEl) projnameEl.select()
         }, 100)
     })

776-784: handleZoom boundary checks ineffective.

Same issue as v1 counterpart. Both conditional branches execute identical code, failing to enforce zoom limits.

🐛 Proposed fix
 function handleZoom(direction) {
-    if (globalScope.scale > 0.5 * DPR) {
-        changeScale(direction * 0.1 * DPR)
-    } else if (globalScope.scale < 4 * DPR) {
-        changeScale(direction * 0.1 * DPR)
-    }
+    const minScale = 0.5 * DPR
+    const maxScale = 4 * DPR
+    if (direction < 0 && globalScope.scale <= minScale) return
+    if (direction > 0 && globalScope.scale >= maxScale) return
+    changeScale(direction * 0.1 * DPR)
     gridUpdateSet(true)
     scheduleUpdate()
 }

791-798: Null check needed before accessing customRange1 at line 792, and duplicate scroll listeners.

Same issues as v1 counterpart:

  1. Line 792 accesses customRange1 before the null check at line 801
  2. Lines 793-798 register scroll handlers that duplicate those at lines 625-630
🐛 Proposed fix for null check
 function zoomSliderListeners() {
-    document.getElementById('customRange1').value = 5
+    const customRange1 = document.getElementById('customRange1')
+    if (!customRange1) return
+    customRange1.value = 5
     document
         .getElementById('simulationArea')
         .addEventListener('DOMMouseScroll', zoomSliderScroll)
     document
         .getElementById('simulationArea')
         .addEventListener('mousewheel', zoomSliderScroll)
-    let curLevel = document.getElementById('customRange1').value
-    const customRange1 = document.getElementById('customRange1')
-    if (customRange1) {
+    let curLevel = customRange1.value

157-158: Bug: Yf uses globalScope.ox instead of globalScope.oy.

Same issue as v1 counterpart. Y-coordinate calculation uses wrong offset.

🐛 Proposed fix
     const Xf = Math.round((RawX - globalScope.ox) / globalScope.scale / unit)
-    const Yf = Math.round((RawY - globalScope.ox) / globalScope.scale / unit)
+    const Yf = Math.round((RawY - globalScope.oy) / globalScope.scale / unit)

132-135: Critical: Math.sqrt() only accepts one argument — Y component is ignored.

Same issue as v1 counterpart. The comma creates a second argument which Math.sqrt() ignores, breaking pinch distance calculation.

🐛 Proposed fix
-    distance = Math.sqrt(
-        (e.touches[1].clientX - e.touches[0].clientX) ** 2,
-        (e.touches[1].clientY - e.touches[0].clientY) ** 2
-    )
+    distance = Math.sqrt(
+        (e.touches[1].clientX - e.touches[0].clientX) ** 2 +
+        (e.touches[1].clientY - e.touches[0].clientY) ** 2
+    )

323-327: Operator precedence bug in mouseX calculation.

Same issue as v1 counterpart. 100 + simulationArea.maxWidth || 0 yields 0 when maxWidth is undefined.

🐛 Proposed fix
-            simulationArea.mouseX = 100 + simulationArea.maxWidth || 0
+            simulationArea.mouseX = 100 + (simulationArea.maxWidth || 0)
🧹 Nitpick comments (2)
src/simulator/src/combinationalAnalysis.js (1)

1-463: This file appears outside the scope of this PR.

This PR's objective is to remove jQuery from listeners.js. The changes in combinationalAnalysis.js are primarily formatting/style normalization (import reformatting, multi-line function signatures) with no jQuery removal. Per the learning that issue #433 requires one-file-per-PR migrations, consider reverting these changes to keep the PR focused.

The static analysis flags (variable redeclarations, eval() usage at line 445) are pre-existing technical debt unrelated to this PR.

v1/src/simulator/src/combinationalAnalysis.js (1)

1-463: Same concern as the non-v1 counterpart — outside PR scope.

This file mirrors the formatting changes in src/simulator/src/combinationalAnalysis.js with no jQuery removal. Consider reverting to keep the PR focused on listeners.js. Based on learnings, issue #433 requires one-file-per-PR migrations.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fd1adbb and c74924f.

📒 Files selected for processing (4)
  • src/simulator/src/combinationalAnalysis.js
  • src/simulator/src/listeners.js
  • v1/src/simulator/src/combinationalAnalysis.js
  • v1/src/simulator/src/listeners.js
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: pantha704
Repo: CircuitVerse/cv-frontend-vue PR: 0
File: :0-0
Timestamp: 2026-01-12T04:23:43.985Z
Learning: Issue `#433` requires jQuery removal to be submitted as one file per PR, keeping each pull request focused on a single file's migration from jQuery to Vue/native DOM APIs.
📚 Learning: 2026-01-13T21:31:27.969Z
Learnt from: naman79820
Repo: CircuitVerse/cv-frontend-vue PR: 791
File: src/simulator/src/setup.js:175-187
Timestamp: 2026-01-13T21:31:27.969Z
Learning: In src/simulator/src/setup.js, startMainListeners() is intentionally NOT called directly in setup(). Instead, it's initialized via Vue lifecycle through setupPanelsWhenReady() (called from Extra.vue onMounted), which waits for all panels to be mounted before attaching listeners. This lifecycle-driven approach prevents race conditions on first Tauri launch.

Applied to files:

  • src/simulator/src/listeners.js
  • v1/src/simulator/src/listeners.js
🧬 Code graph analysis (3)
src/simulator/src/listeners.js (5)
v1/src/simulator/src/listeners.js (13)
  • coordinate (45-45)
  • returnCoordinate (46-49)
  • currDistance (51-51)
  • distance (52-52)
  • pinchZ (53-53)
  • centreX (54-54)
  • centreY (55-55)
  • timeout (56-56)
  • lastTap (57-57)
  • pinchZoom (124-165)
  • i (292-292)
  • startListeners (332-760)
  • subcircuitMenu (716-716)
src/simulator/src/engine.js (3)
  • updateCanvasSet (114-116)
  • scheduleUpdate (465-484)
  • update (495-570)
src/simulator/src/embedListeners.js (3)
  • simulationArea (104-104)
  • i (106-106)
  • startListeners (124-255)
src/simulator/src/canvasApi.js (1)
  • findDimensions (10-58)
src/simulator/src/data.js (1)
  • logixFunction (24-24)
v1/src/simulator/src/listeners.js (8)
src/simulator/src/engine.js (1)
  • updateCanvasSet (114-116)
src/simulator/src/restrictedElementDiv.js (3)
  • globalScope (4-4)
  • showRestricted (35-40)
  • hideRestricted (42-44)
src/simulator/src/data/backupCircuit.js (1)
  • scheduleBackup (72-85)
src/store/simulatorMobileStore.ts (1)
  • useSimulatorMobileStore (6-36)
src/simulator/src/minimap.js (2)
  • updatelastMinimapShown (175-177)
  • removeMiniMap (178-194)
src/simulator/src/canvasApi.js (1)
  • findDimensions (10-58)
src/simulator/src/ux.js (2)
  • exitFullView (405-424)
  • element (484-484)
src/simulator/src/data.js (1)
  • logixFunction (24-24)
v1/src/simulator/src/combinationalAnalysis.js (7)
src/simulator/src/utils.ts (1)
  • stripTags (38-40)
src/simulator/src/quinMcCluskey.ts (1)
  • BooleanMinimize (10-54)
src/simulator/src/canvasApi.js (1)
  • findDimensions (10-58)
src/simulator/src/node.js (1)
  • Node (150-1034)
src/simulator/src/modules/NotGate.js (1)
  • NotGate (18-96)
src/simulator/src/modules/AndGate.js (1)
  • AndGate (21-136)
src/simulator/src/modules/OrGate.ts (1)
  • OrGate (10-121)
🪛 Biome (2.1.2)
src/simulator/src/combinationalAnalysis.js

[error] 179-179: Shouldn't redeclare 'i'. Consider to delete it or rename it.

'i' is defined here:

(lint/suspicious/noRedeclare)


[error] 231-231: Shouldn't redeclare 'i'. Consider to delete it or rename it.

'i' is defined here:

(lint/suspicious/noRedeclare)


[error] 263-263: Shouldn't redeclare 'k'. Consider to delete it or rename it.

'k' is defined here:

(lint/suspicious/noRedeclare)


[error] 265-265: Shouldn't redeclare 'index'. Consider to delete it or rename it.

'index' is defined here:

(lint/suspicious/noRedeclare)


[error] 272-272: Shouldn't redeclare 'v'. Consider to delete it or rename it.

'v' is defined here:

(lint/suspicious/noRedeclare)


[error] 307-307: Shouldn't redeclare 'j'. Consider to delete it or rename it.

'j' is defined here:

(lint/suspicious/noRedeclare)


[error] 308-308: Shouldn't redeclare 'v'. Consider to delete it or rename it.

'v' is defined here:

(lint/suspicious/noRedeclare)


[error] 315-315: Shouldn't redeclare 'v2'. Consider to delete it or rename it.

'v2' is defined here:

(lint/suspicious/noRedeclare)


[error] 445-445: eval() exposes to security risks and performance issues.

See the MDN web docs for more details.
Refactor the code so that it doesn't need to call eval().

(lint/security/noGlobalEval)

v1/src/simulator/src/combinationalAnalysis.js

[error] 179-179: Shouldn't redeclare 'i'. Consider to delete it or rename it.

'i' is defined here:

(lint/suspicious/noRedeclare)


[error] 231-231: Shouldn't redeclare 'i'. Consider to delete it or rename it.

'i' is defined here:

(lint/suspicious/noRedeclare)


[error] 263-263: Shouldn't redeclare 'k'. Consider to delete it or rename it.

'k' is defined here:

(lint/suspicious/noRedeclare)


[error] 265-265: Shouldn't redeclare 'index'. Consider to delete it or rename it.

'index' is defined here:

(lint/suspicious/noRedeclare)


[error] 272-272: Shouldn't redeclare 'v'. Consider to delete it or rename it.

'v' is defined here:

(lint/suspicious/noRedeclare)


[error] 307-307: Shouldn't redeclare 'j'. Consider to delete it or rename it.

'j' is defined here:

(lint/suspicious/noRedeclare)


[error] 308-308: Shouldn't redeclare 'v'. Consider to delete it or rename it.

'v' is defined here:

(lint/suspicious/noRedeclare)


[error] 315-315: Shouldn't redeclare 'v2'. Consider to delete it or rename it.

'v2' is defined here:

(lint/suspicious/noRedeclare)


[error] 445-445: eval() exposes to security risks and performance issues.

See the MDN web docs for more details.
Refactor the code so that it doesn't need to call eval().

(lint/security/noGlobalEval)

🔇 Additional comments (2)
v1/src/simulator/src/listeners.js (1)

785-790: LGTM — ZoomIn() and ZoomOut() exports are clean.

The public API additions delegate correctly to handleZoom with appropriate direction values.

src/simulator/src/listeners.js (1)

785-790: LGTM — ZoomIn() and ZoomOut() exports.

Clean public API additions for zoom functionality.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines +132 to +135
distance = Math.sqrt(
(e.touches[1].clientX - e.touches[0].clientX) ** 2,
(e.touches[1].clientY - e.touches[0].clientY) ** 2
)
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Critical: Math.sqrt() only accepts one argument — Y component is ignored.

The comma creates a second argument which Math.sqrt() ignores, resulting in incorrect pinch distance calculation. This breaks pinch-to-zoom functionality.

🐛 Proposed fix
-    distance = Math.sqrt(
-        (e.touches[1].clientX - e.touches[0].clientX) ** 2,
-        (e.touches[1].clientY - e.touches[0].clientY) ** 2
-    )
+    distance = Math.sqrt(
+        (e.touches[1].clientX - e.touches[0].clientX) ** 2 +
+        (e.touches[1].clientY - e.touches[0].clientY) ** 2
+    )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
distance = Math.sqrt(
(e.touches[1].clientX - e.touches[0].clientX) ** 2,
(e.touches[1].clientY - e.touches[0].clientY) ** 2
)
distance = Math.sqrt(
(e.touches[1].clientX - e.touches[0].clientX) ** 2 +
(e.touches[1].clientY - e.touches[0].clientY) ** 2
)
🤖 Prompt for AI Agents
In `@v1/src/simulator/src/listeners.js` around lines 132 - 135, The pinch distance
calculation incorrectly passes two arguments to Math.sqrt (so the Y component is
ignored); update the calculation that assigns to distance in listeners.js to
compute the Euclidean distance by summing the squared X and Y differences and
passing that single sum to Math.sqrt (use e.touches[1]/[0] clientX and clientY
values), ensuring the expression is wrapped so Math.sqrt receives one numeric
argument.

Comment on lines +157 to +158
const Xf = Math.round((RawX - globalScope.ox) / globalScope.scale / unit)
const Yf = Math.round((RawY - globalScope.ox) / globalScope.scale / unit)
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Bug: Yf uses globalScope.ox instead of globalScope.oy.

The Y-coordinate calculation incorrectly uses the X-offset, causing pinch zoom recentering to malfunction.

🐛 Proposed fix
     const Xf = Math.round((RawX - globalScope.ox) / globalScope.scale / unit)
-    const Yf = Math.round((RawY - globalScope.ox) / globalScope.scale / unit)
+    const Yf = Math.round((RawY - globalScope.oy) / globalScope.scale / unit)
🤖 Prompt for AI Agents
In `@v1/src/simulator/src/listeners.js` around lines 157 - 158, The Yf calculation
is using the X offset (globalScope.ox) by mistake; update the Yf formula to
subtract the Y offset (globalScope.oy) instead of globalScope.ox so it mirrors
the Xf logic: compute Yf = Math.round((RawY - globalScope.oy) /
globalScope.scale / unit) while keeping RawY, globalScope.scale and unit
unchanged to fix pinch-zoom recentering; locate and change the line that defines
Yf in listeners.js where Xf and Yf are computed.

Comment on lines 323 to +327
if (!isCopy.value) {
findDimensions(globalScope);
simulationArea.mouseX = 100 + simulationArea.maxWidth || 0;
simulationArea.mouseY = simulationArea.minHeight || 0;
getTap(e);
findDimensions(globalScope)
simulationArea.mouseX = 100 + simulationArea.maxWidth || 0
simulationArea.mouseY = simulationArea.minHeight || 0
getTap(e)
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Operator precedence bug: loses base value when maxWidth is undefined.

100 + undefined yields NaN, then NaN || 0 returns 0 instead of the intended 100. The || should apply only to maxWidth.

🐛 Proposed fix
-            simulationArea.mouseX = 100 + simulationArea.maxWidth || 0
-            simulationArea.mouseY = simulationArea.minHeight || 0
+            simulationArea.mouseX = 100 + (simulationArea.maxWidth || 0)
+            simulationArea.mouseY = simulationArea.minHeight || 0
🤖 Prompt for AI Agents
In `@v1/src/simulator/src/listeners.js` around lines 323 - 327, The assignment to
simulationArea.mouseX uses incorrect operator precedence: "100 +
simulationArea.maxWidth || 0" causes 100 + undefined -> NaN then || 0 returns 0;
change it so the default applies only to maxWidth (e.g., compute 100 +
(simulationArea.maxWidth || 0)). Update the expression in the block guarded by
isCopy.value (near findDimensions and getTap) to parenthesize the maxWidth
defaulting; leave mouseY's existing fallback for minHeight as-is unless you also
want the same explicit grouping.

@pantha704
Copy link
Contributor Author

I've rebased from main and kept this PR strictly to jQuery changes. No formatting stuff this time.

Replaced all the jQuery calls with native DOM methods and made sure v1/src has the same changes.

About those bugs CodeRabbit flagged (the Math.sqrt and oy coordinate issues) - those are pre-existing in the codebase, not from my changes. Left them as-is to keep this PR focused, I can fix them in a separate PR if you want.

Let me know if anything else needs fixing!

@pantha704
Copy link
Contributor Author

@tachyons should i create a new issue for the formatting bugs by coderabbit,
or is it supposed to be left as it is?

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Removal of JQuery to Vue's reactives

2 participants