commit 906fb3fc22b1e865659a083d4de02d38b5bddd8c
parent 204010b9842201c033a1fab89cc95c593543f71e
Author: Hunter
Date: Wed, 29 Jul 2026 15:17:56 -0400
clamp dropped images to grid bounds instead of wrapping to 0,0
Diffstat:
2 files changed, 25 insertions(+), 20 deletions(-)
diff --git a/resources/images.js b/resources/images.js
@@ -79,24 +79,31 @@ async function processAndAddImages(files, dropX = 0, dropY = 0) {
imageDataArray.push({ idx, ...data });
}
+ // Shift the whole fan back onto the grid before placing anything, so a drop near an edge keeps
+ // its diagonal spacing instead of collapsing image by image
+ const fanMaxX = Math.max(...imageDataArray.map(({ idx, widthCells }) => baseXCell + idx + widthCells));
+ const fanMaxY = Math.max(...imageDataArray.map(({ idx, heightCells }) => baseYCell + idx + heightCells));
+ const fanShiftX = Math.max(Math.min(0, GRID_COLS - fanMaxX), -baseXCell);
+ const fanShiftY = Math.max(Math.min(0, GRID_ROWS - fanMaxY), -baseYCell);
+
let wrappedOffset = 0;
+ const placed = [];
for (const { idx, dataUrl, widthCells, heightCells, naturalWidth, naturalHeight } of imageDataArray) {
- // Calculate position with diagonal offset
- let xCell = baseXCell + idx;
- let yCell = baseYCell + idx;
-
- // If out of bounds or would overlap with wrapped images, wrap to next diagonal position
- const outOfBounds = xCell < 0 || yCell < 0 ||
- xCell + widthCells > GRID_COLS ||
- yCell + heightCells > GRID_ROWS;
- const overlapsWrapped = xCell < wrappedOffset || yCell < wrappedOffset;
-
- if (outOfBounds || overlapsWrapped) {
- xCell = wrappedOffset;
- yCell = wrappedOffset;
+ // Calculate position with diagonal offset, kept as close to the drop point as the grid allows
+ const maxXCell = GRID_COLS - widthCells;
+ const maxYCell = GRID_ROWS - heightCells;
+ let xCell = Math.max(0, Math.min(baseXCell + idx + fanShiftX, maxXCell));
+ let yCell = Math.max(0, Math.min(baseYCell + idx + fanShiftY, maxYCell));
+
+ // Clamping can pile a multi-image drop onto one cell; cascade those from the top-left instead
+ const stacked = placed.some(p => p.xCell === xCell && p.yCell === yCell);
+ if (stacked) {
+ xCell = Math.min(wrappedOffset, maxXCell);
+ yCell = Math.min(wrappedOffset, maxYCell);
wrappedOffset++;
}
+ placed.push({ xCell, yCell });
const imageData = addImage(dataUrl, xCell, yCell, widthCells, heightCells, { naturalWidth, naturalHeight });
imageData.container.style.zIndex = baseZIndex + idx;
diff --git a/resources/input.js b/resources/input.js
@@ -26,15 +26,13 @@ document.addEventListener('paste', async (e) => {
return;
}
- // Use last known mouse position relative to grid, fall back to (0, 0)
+ // Use last known mouse position relative to grid; off-grid positions are clamped downstream,
+ // so only a cursor we've never seen falls back to (0, 0)
const gridRect = grid.getBoundingClientRect();
- const mouseXInGrid = lastMouseX - gridRect.left;
- const mouseYInGrid = lastMouseY - gridRect.top;
- const isOnGrid = mouseXInGrid >= 0 && mouseYInGrid >= 0 &&
- mouseXInGrid <= gridRect.width && mouseYInGrid <= gridRect.height;
+ const hasCursor = lastMouseX >= 0 && lastMouseY >= 0;
- const dropX = isOnGrid ? mouseXInGrid : 0;
- const dropY = isOnGrid ? mouseYInGrid : 0;
+ const dropX = hasCursor ? lastMouseX - gridRect.left : 0;
+ const dropY = hasCursor ? lastMouseY - gridRect.top : 0;
await processAndAddImages(imageFiles, dropX, dropY);
});