duplicate image on cmd+click
Hunter
Monday, October 20th, 2025 at 12:32 PM
185 grid.appendChild(container);
186
187 setupImageHandlers(imageData);
188 +
189 + return imageData;
190 }
191
192 function calculatePanBounds(imageData) {
261 bringToFront(container);
262 });
263
- // Moving / Deleting
264 + // Moving / Deleting / Duplicating
265 container.addEventListener('mousedown', (e) => {
266 if (e.target.classList.contains('resize-handle')) return;
267
277 return;
278 }
279
280 + // Cmd-click (or Ctrl-click on Windows/Linux) to duplicate
281 + if (e.metaKey || e.ctrlKey) {
282 + // Calculate target position (2 cells to the right if there's room)
283 + let newXCell = imageData.xCell + 2;
284 + let newYCell = imageData.yCell;
285 +
286 + // If there's not enough room to the right, try to place appropriately
287 + if (newXCell + imageData.widthCells > GRID_COLS) {
288 + // Try wrapping to next row
289 + newXCell = 0;
290 + newYCell = imageData.yCell + imageData.heightCells;
291 +
292 + // If that goes off the bottom, place at origin
293 + if (newYCell + imageData.heightCells > GRID_ROWS) {
294 + newXCell = 0;
295 + newYCell = 0;
296 + }
297 + }
298 +
299 + // Create duplicate with the same image source and dimensions
300 + const imgElement = container.querySelector('img');
301 + const newImageData = addImage(
302 + imgElement.src,
303 + newXCell,
304 + newYCell,
305 + imageData.widthCells,
306 + imageData.heightCells
307 + );
308 +
309 + // Copy pan and zoom settings from original
310 + // Store the original settings to apply after image loads
311 + const originalPanX = imageData.panX;
312 + const originalPanY = imageData.panY;
313 + const originalUserScale = imageData.userScale;
314 +
315 + // Override the onload to copy settings
316 + const newImg = newImageData.container.querySelector('img');
317 + const originalOnload = newImg.onload;
318 + newImg.onload = () => {
319 + // Run the original onload first
320 + if (originalOnload) originalOnload.call(newImg);
321 +
322 + // Then apply the copied settings
323 + newImageData.panX = originalPanX;
324 + newImageData.panY = originalPanY;
325 + newImageData.userScale = originalUserScale;
326 + updateImagePosition(newImageData);
327 + };
328 +
329 + // If image is already loaded (cached), trigger the settings copy
330 + if (newImg.complete && newImageData.naturalWidth > 0) {
331 + newImageData.panX = originalPanX;
332 + newImageData.panY = originalPanY;
333 + newImageData.userScale = originalUserScale;
334 + updateImagePosition(newImageData);
335 + }
336 +
337 + return;
338 + }
339 +
340 // Lock cursor to move during drag operation
341 document.body.style.cursor = 'move';
342 document.body.classList.add('dragging');