From 9f18f1bbc07c9f345b4ecb067107c905c75ac0b5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Feb 2026 06:49:05 +0000 Subject: [PATCH 1/3] Initial plan From 22a6bd380b1f95890c21a287603234cc5066dc79 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Feb 2026 06:54:25 +0000 Subject: [PATCH 2/3] Fix touch dragging by preventing re-renders during drag operations Co-authored-by: A1L13N <193832434+A1L13N@users.noreply.github.com> --- css/styles.css | 2 ++ js/modules/Canvas.js | 42 ++++++++++++++++++++++++++++++++++---- js/modules/StateManager.js | 17 +++++++++++++-- 3 files changed, 55 insertions(+), 6 deletions(-) diff --git a/css/styles.css b/css/styles.css index 7f9a60e..44b65be 100644 --- a/css/styles.css +++ b/css/styles.css @@ -435,6 +435,7 @@ body { position: absolute; cursor: move; user-select: none; + touch-action: none; /* Allow custom touch handling for dragging */ } .element.selected { @@ -616,6 +617,7 @@ body { font-size: 13px; color: #333; font-family: inherit; + touch-action: auto; /* Allow normal touch interactions for inputs */ } .property-group textarea { diff --git a/js/modules/Canvas.js b/js/modules/Canvas.js index 2c19e2d..12d53dc 100644 --- a/js/modules/Canvas.js +++ b/js/modules/Canvas.js @@ -268,7 +268,14 @@ class Canvas { if (this.isDragging && this.state.selectedElementId && this.dragStart) { const newX = (e.clientX - this.dragStart.x) / this.state.zoom; const newY = (e.clientY - this.dragStart.y) / this.state.zoom; - this.state.updateElement(this.state.selectedElementId, { x: newX, y: newY }); + this.state.updateElementSilent(this.state.selectedElementId, { x: newX, y: newY }); + + // Manually update DOM element position for smooth dragging + const domElement = this.content.querySelector(`[data-id="${this.state.selectedElementId}"]`); + if (domElement) { + domElement.style.left = newX + 'px'; + domElement.style.top = newY + 'px'; + } } else if (this.isResizing && this.state.selectedElementId && this.resizeStart) { const dx = e.clientX - this.resizeStart.x; @@ -293,7 +300,16 @@ class Canvas { updates.y = this.resizeStart.elementY + (this.resizeStart.elementHeight - newHeight); } - this.state.updateElement(this.state.selectedElementId, updates); + this.state.updateElementSilent(this.state.selectedElementId, updates); + + // Manually update DOM element for smooth resizing + const domElement = this.content.querySelector(`[data-id="${this.state.selectedElementId}"]`); + if (domElement) { + if (updates.x !== undefined) domElement.style.left = updates.x + 'px'; + if (updates.y !== undefined) domElement.style.top = updates.y + 'px'; + if (updates.width !== undefined) domElement.style.width = updates.width + 'px'; + if (updates.height !== undefined) domElement.style.height = updates.height + 'px'; + } } // Update cursor @@ -305,6 +321,7 @@ class Canvas { handleMouseUp() { if (this.isDragging || this.isResizing) { this.state.saveHistory(); + this.state.notify(); // Trigger re-render with final positions this.isDragging = false; this.isResizing = false; this.dragStart = null; @@ -376,7 +393,14 @@ class Canvas { const touch = e.touches[0]; const newX = (touch.clientX - this.dragStart.x) / this.state.zoom; const newY = (touch.clientY - this.dragStart.y) / this.state.zoom; - this.state.updateElement(this.state.selectedElementId, { x: newX, y: newY }); + this.state.updateElementSilent(this.state.selectedElementId, { x: newX, y: newY }); + + // Manually update DOM element position for smooth dragging + const domElement = this.content.querySelector(`[data-id="${this.state.selectedElementId}"]`); + if (domElement) { + domElement.style.left = newX + 'px'; + domElement.style.top = newY + 'px'; + } } else if (this.isResizing && this.state.selectedElementId && this.resizeStart) { e.preventDefault(); @@ -403,7 +427,16 @@ class Canvas { updates.y = this.resizeStart.elementY + (this.resizeStart.elementHeight - newHeight); } - this.state.updateElement(this.state.selectedElementId, updates); + this.state.updateElementSilent(this.state.selectedElementId, updates); + + // Manually update DOM element for smooth resizing + const domElement = this.content.querySelector(`[data-id="${this.state.selectedElementId}"]`); + if (domElement) { + if (updates.x !== undefined) domElement.style.left = updates.x + 'px'; + if (updates.y !== undefined) domElement.style.top = updates.y + 'px'; + if (updates.width !== undefined) domElement.style.width = updates.width + 'px'; + if (updates.height !== undefined) domElement.style.height = updates.height + 'px'; + } } // Update cursor @@ -415,6 +448,7 @@ class Canvas { handleTouchEnd() { if (this.isDragging || this.isResizing) { this.state.saveHistory(); + this.state.notify(); // Trigger re-render with final positions this.isDragging = false; this.isResizing = false; this.dragStart = null; diff --git a/js/modules/StateManager.js b/js/modules/StateManager.js index 2787d41..f580383 100644 --- a/js/modules/StateManager.js +++ b/js/modules/StateManager.js @@ -159,6 +159,17 @@ class StateManager { } } + // Update element without triggering notification (for dragging/resizing) + updateElementSilent(id, updates) { + const page = this.getCurrentPage(); + if (!page) return; + + const element = page.elements.find(el => el.id === id); + if (element) { + Object.assign(element, updates); + } + } + // Delete element deleteElement(id) { const page = this.getCurrentPage(); @@ -174,8 +185,10 @@ class StateManager { // Select element selectElement(id) { - this.selectedElementId = id; - this.notify(); + if (this.selectedElementId !== id) { + this.selectedElementId = id; + this.notify(); + } } // Deselect element From ad99008cda5e0607156f7bb4d7002bdd3ae65b26 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Feb 2026 06:59:13 +0000 Subject: [PATCH 3/3] Refactor: Extract helper methods to reduce code duplication Co-authored-by: A1L13N <193832434+A1L13N@users.noreply.github.com> --- js/modules/Canvas.js | 53 ++++++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/js/modules/Canvas.js b/js/modules/Canvas.js index 12d53dc..34da1a2 100644 --- a/js/modules/Canvas.js +++ b/js/modules/Canvas.js @@ -233,6 +233,31 @@ class Canvas { }); } + // Helper method to get DOM node for an element + getElementDomNode(elementId) { + return this.content.querySelector(`[data-id="${elementId}"]`); + } + + // Helper method to update element position in DOM + updateElementPosition(elementId, x, y) { + const domElement = this.getElementDomNode(elementId); + if (domElement) { + domElement.style.left = x + 'px'; + domElement.style.top = y + 'px'; + } + } + + // Helper method to update element styles in DOM + updateElementStyles(elementId, updates) { + const domElement = this.getElementDomNode(elementId); + if (domElement) { + if (updates.x !== undefined) domElement.style.left = updates.x + 'px'; + if (updates.y !== undefined) domElement.style.top = updates.y + 'px'; + if (updates.width !== undefined) domElement.style.width = updates.width + 'px'; + if (updates.height !== undefined) domElement.style.height = updates.height + 'px'; + } + } + handleElementMouseDown(e, elementId) { if (e.button !== 0) return; e.stopPropagation(); @@ -271,11 +296,7 @@ class Canvas { this.state.updateElementSilent(this.state.selectedElementId, { x: newX, y: newY }); // Manually update DOM element position for smooth dragging - const domElement = this.content.querySelector(`[data-id="${this.state.selectedElementId}"]`); - if (domElement) { - domElement.style.left = newX + 'px'; - domElement.style.top = newY + 'px'; - } + this.updateElementPosition(this.state.selectedElementId, newX, newY); } else if (this.isResizing && this.state.selectedElementId && this.resizeStart) { const dx = e.clientX - this.resizeStart.x; @@ -303,13 +324,7 @@ class Canvas { this.state.updateElementSilent(this.state.selectedElementId, updates); // Manually update DOM element for smooth resizing - const domElement = this.content.querySelector(`[data-id="${this.state.selectedElementId}"]`); - if (domElement) { - if (updates.x !== undefined) domElement.style.left = updates.x + 'px'; - if (updates.y !== undefined) domElement.style.top = updates.y + 'px'; - if (updates.width !== undefined) domElement.style.width = updates.width + 'px'; - if (updates.height !== undefined) domElement.style.height = updates.height + 'px'; - } + this.updateElementStyles(this.state.selectedElementId, updates); } // Update cursor @@ -396,11 +411,7 @@ class Canvas { this.state.updateElementSilent(this.state.selectedElementId, { x: newX, y: newY }); // Manually update DOM element position for smooth dragging - const domElement = this.content.querySelector(`[data-id="${this.state.selectedElementId}"]`); - if (domElement) { - domElement.style.left = newX + 'px'; - domElement.style.top = newY + 'px'; - } + this.updateElementPosition(this.state.selectedElementId, newX, newY); } else if (this.isResizing && this.state.selectedElementId && this.resizeStart) { e.preventDefault(); @@ -430,13 +441,7 @@ class Canvas { this.state.updateElementSilent(this.state.selectedElementId, updates); // Manually update DOM element for smooth resizing - const domElement = this.content.querySelector(`[data-id="${this.state.selectedElementId}"]`); - if (domElement) { - if (updates.x !== undefined) domElement.style.left = updates.x + 'px'; - if (updates.y !== undefined) domElement.style.top = updates.y + 'px'; - if (updates.width !== undefined) domElement.style.width = updates.width + 'px'; - if (updates.height !== undefined) domElement.style.height = updates.height + 'px'; - } + this.updateElementStyles(this.state.selectedElementId, updates); } // Update cursor