center spawned images under cursor; playing card layout for multiple images

Hunter
Sunday, October 19th, 2025 at 9:39 PM

index.html+41-6

index.html
238  			e.stopPropagation();
239  		});
240  
    -		page.addEventListener('drop', (e) => {
241 +		page.addEventListener('drop', async (e) => {
242  			e.preventDefault();
243  			e.stopPropagation();
244  

249  			const dropX = e.clientX - gridRect.left;
250  			const dropY = e.clientY - gridRect.top;
251  
252 +			// Load first image to get base dimensions
253 +			let firstImageDimensions = null;
254 +			if (files.length > 0) {
255 +				const firstFile = files[0];
256 +				const reader = new FileReader();
257 +				const dataUrl = await new Promise(resolve => {
258 +					reader.onload = (e) => resolve(e.target.result);
259 +					reader.readAsDataURL(firstFile);
260 +				});
261 +
262 +				const img = new Image();
263 +				await new Promise(resolve => {
264 +					img.onload = () => {
265 +						const aspectRatio = img.width / img.height;
266 +						let widthCells, heightCells;
267 +						if (aspectRatio >= 1) {
268 +							heightCells = 5;
269 +							widthCells = Math.round(heightCells * aspectRatio);
270 +						} else {
271 +							widthCells = 5;
272 +							heightCells = Math.round(widthCells / aspectRatio);
273 +						}
274 +						widthCells = Math.min(widthCells, GRID_COLS);
275 +						heightCells = Math.min(heightCells, GRID_ROWS);
276 +						firstImageDimensions = { widthCells, heightCells };
277 +						resolve();
278 +					};
279 +					img.src = dataUrl;
280 +				});
281 +			}
282 +
283 +			// Now process all files
284  			files.forEach((file, idx) => {
285  				const reader = new FileReader();
286  				reader.onload = (event) => {

304  						widthCells = Math.min(widthCells, GRID_COLS);
305  						heightCells = Math.min(heightCells, GRID_ROWS);
306  
    -						// Calculate position near drop point
    -						let xCell = Math.round(dropX / CELL_SIZE_PX) + (idx * 2);
    -						let yCell = Math.round(dropY / CELL_SIZE_PX);
307 +						// Calculate position with first image's center under drop point
308 +						let xCell = Math.round(dropX / CELL_SIZE_PX - firstImageDimensions.widthCells / 2) + (idx * 2);
309 +						let yCell = Math.round(dropY / CELL_SIZE_PX - firstImageDimensions.heightCells / 2);
310  
311  						// Ensure image stays within grid bounds
312  						xCell = Math.max(0, Math.min(GRID_COLS - widthCells, xCell));
313  						yCell = Math.max(0, Math.min(GRID_ROWS - heightCells, yCell));
314  
    -						addImage(event.target.result, xCell, yCell, widthCells, heightCells);
315 +						addImage(event.target.result, xCell, yCell, widthCells, heightCells, idx + 1);
316  					};
317  					img.src = event.target.result;
318  				};

320  			});
321  		});
322  
    -		function addImage(src, xCell, yCell, widthCells, heightCells) {
323 +		function addImage(src, xCell, yCell, widthCells, heightCells, zIndex = 0) {
324  			const container = document.createElement('div');
325  			container.className = 'image-container';
326 +			if (zIndex > 0) {
327 +				container.style.zIndex = zIndex;
328 +			}
329  
330  			const img = document.createElement('img');
331  			img.src = src;