diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/__pycache__/__init__.cpython-312.pyc b/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..01373a5 Binary files /dev/null and b/__pycache__/__init__.cpython-312.pyc differ diff --git a/__pycache__/__init__.cpython-313.pyc b/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..635ed18 Binary files /dev/null and b/__pycache__/__init__.cpython-313.pyc differ diff --git a/__pycache__/canvas_node.cpython-312.pyc b/__pycache__/canvas_node.cpython-312.pyc new file mode 100644 index 0000000..3491ccd Binary files /dev/null and b/__pycache__/canvas_node.cpython-312.pyc differ diff --git a/__pycache__/canvas_node.cpython-313.pyc b/__pycache__/canvas_node.cpython-313.pyc new file mode 100644 index 0000000..9abddf1 Binary files /dev/null and b/__pycache__/canvas_node.cpython-313.pyc differ diff --git a/js/CanvasInteractions.js b/js/CanvasInteractions.js index ef6ec67..4290a78 100644 --- a/js/CanvasInteractions.js +++ b/js/CanvasInteractions.js @@ -192,6 +192,12 @@ export class CanvasInteractions { } handleMouseDown(e) { this.canvas.canvas.focus(); + // Sync modifier states with actual event state to prevent "stuck" modifiers + // when focus moves between layers panel and canvas + this.interaction.isCtrlPressed = e.ctrlKey; + this.interaction.isMetaPressed = e.metaKey; + this.interaction.isShiftPressed = e.shiftKey; + this.interaction.isAltPressed = e.altKey; const coords = this.getMouseCoordinates(e); const mods = this.getModifierState(e); if (this.interaction.mode === 'drawingMask') { @@ -738,12 +744,11 @@ export class CanvasInteractions { if (mods.ctrl || mods.meta) { const index = this.canvas.canvasSelection.selectedLayers.indexOf(layer); if (index === -1) { + // Ctrl-clicking unselected layer: add to selection this.canvas.canvasSelection.updateSelection([...this.canvas.canvasSelection.selectedLayers, layer]); } - else { - const newSelection = this.canvas.canvasSelection.selectedLayers.filter((l) => l !== layer); - this.canvas.canvasSelection.updateSelection(newSelection); - } + // If already selected, do NOT deselect - allows dragging multiple layers with Ctrl held + // User can use right-click in layers panel to deselect individual layers } else { if (!this.canvas.canvasSelection.selectedLayers.includes(layer)) { diff --git a/js/CanvasLayersPanel.js b/js/CanvasLayersPanel.js index fa989bd..0e2f46c 100644 --- a/js/CanvasLayersPanel.js +++ b/js/CanvasLayersPanel.js @@ -123,6 +123,26 @@ export class CanvasLayersPanel { e.preventDefault(); e.stopPropagation(); this.deleteSelectedLayers(); + return; + } + // Handle Ctrl+C/V for layer copy/paste when panel has focus + if (e.ctrlKey || e.metaKey) { + if (e.key.toLowerCase() === 'c') { + if (this.canvas.canvasSelection.selectedLayers.length > 0) { + e.preventDefault(); + e.stopPropagation(); + this.canvas.canvasLayers.copySelectedLayers(); + log.info('Layers copied from panel'); + } + } + else if (e.key.toLowerCase() === 'v') { + e.preventDefault(); + e.stopPropagation(); + if (this.canvas.canvasLayers.internalClipboard.length > 0) { + this.canvas.canvasLayers.pasteLayers(); + log.info('Layers pasted from panel'); + } + } } }); log.debug('Panel structure created'); diff --git a/python/__pycache__/__init__.cpython-312.pyc b/python/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..ae77ad4 Binary files /dev/null and b/python/__pycache__/__init__.cpython-312.pyc differ diff --git a/python/__pycache__/__init__.cpython-313.pyc b/python/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..11a7cce Binary files /dev/null and b/python/__pycache__/__init__.cpython-313.pyc differ diff --git a/python/__pycache__/config.cpython-312.pyc b/python/__pycache__/config.cpython-312.pyc new file mode 100644 index 0000000..bda3fd4 Binary files /dev/null and b/python/__pycache__/config.cpython-312.pyc differ diff --git a/python/__pycache__/config.cpython-313.pyc b/python/__pycache__/config.cpython-313.pyc new file mode 100644 index 0000000..d4550b3 Binary files /dev/null and b/python/__pycache__/config.cpython-313.pyc differ diff --git a/python/__pycache__/logger.cpython-312.pyc b/python/__pycache__/logger.cpython-312.pyc new file mode 100644 index 0000000..2f71517 Binary files /dev/null and b/python/__pycache__/logger.cpython-312.pyc differ diff --git a/python/__pycache__/logger.cpython-313.pyc b/python/__pycache__/logger.cpython-313.pyc new file mode 100644 index 0000000..dcb1a72 Binary files /dev/null and b/python/__pycache__/logger.cpython-313.pyc differ diff --git a/src/CanvasInteractions.ts b/src/CanvasInteractions.ts index 8f39703..bc1004a 100644 --- a/src/CanvasInteractions.ts +++ b/src/CanvasInteractions.ts @@ -283,6 +283,14 @@ export class CanvasInteractions { handleMouseDown(e: MouseEvent): void { this.canvas.canvas.focus(); + + // Sync modifier states with actual event state to prevent "stuck" modifiers + // when focus moves between layers panel and canvas + this.interaction.isCtrlPressed = e.ctrlKey; + this.interaction.isMetaPressed = e.metaKey; + this.interaction.isShiftPressed = e.shiftKey; + this.interaction.isAltPressed = e.altKey; + const coords = this.getMouseCoordinates(e); const mods = this.getModifierState(e); @@ -873,11 +881,11 @@ export class CanvasInteractions { if (mods.ctrl || mods.meta) { const index = this.canvas.canvasSelection.selectedLayers.indexOf(layer); if (index === -1) { + // Ctrl-clicking unselected layer: add to selection this.canvas.canvasSelection.updateSelection([...this.canvas.canvasSelection.selectedLayers, layer]); - } else { - const newSelection = this.canvas.canvasSelection.selectedLayers.filter((l: Layer) => l !== layer); - this.canvas.canvasSelection.updateSelection(newSelection); } + // If already selected, do NOT deselect - allows dragging multiple layers with Ctrl held + // User can use right-click in layers panel to deselect individual layers } else { if (!this.canvas.canvasSelection.selectedLayers.includes(layer)) { this.canvas.canvasSelection.updateSelection([layer]); diff --git a/src/CanvasLayersPanel.ts b/src/CanvasLayersPanel.ts index 3f91c49..6626a5e 100644 --- a/src/CanvasLayersPanel.ts +++ b/src/CanvasLayersPanel.ts @@ -144,6 +144,26 @@ export class CanvasLayersPanel { e.preventDefault(); e.stopPropagation(); this.deleteSelectedLayers(); + return; + } + + // Handle Ctrl+C/V for layer copy/paste when panel has focus + if (e.ctrlKey || e.metaKey) { + if (e.key.toLowerCase() === 'c') { + if (this.canvas.canvasSelection.selectedLayers.length > 0) { + e.preventDefault(); + e.stopPropagation(); + this.canvas.canvasLayers.copySelectedLayers(); + log.info('Layers copied from panel'); + } + } else if (e.key.toLowerCase() === 'v') { + e.preventDefault(); + e.stopPropagation(); + if (this.canvas.canvasLayers.internalClipboard.length > 0) { + this.canvas.canvasLayers.pasteLayers(); + log.info('Layers pasted from panel'); + } + } } });