diff --git a/js/BatchPreviewManager.js b/js/BatchPreviewManager.js index 4a54999..3cdbf72 100644 --- a/js/BatchPreviewManager.js +++ b/js/BatchPreviewManager.js @@ -3,7 +3,7 @@ import {createModuleLogger} from "./utils/LoggerUtils.js"; const log = createModuleLogger('BatchPreviewManager'); export class BatchPreviewManager { - constructor(canvas) { + constructor(canvas, initialPosition = { x: 0, y: 0 }) { this.canvas = canvas; this.active = false; this.layers = []; @@ -13,8 +13,8 @@ export class BatchPreviewManager { this.maskWasVisible = false; // Position in canvas world coordinates - this.worldX = 0; - this.worldY = 0; + this.worldX = initialPosition.x; + this.worldY = initialPosition.y; this.isDragging = false; } @@ -143,13 +143,6 @@ export class BatchPreviewManager { this._createUI(); - // Set initial position to be centered horizontally and just below the output area - const menuWidthInWorld = this.element.offsetWidth / this.canvas.viewport.zoom; - const paddingInWorld = 20 / this.canvas.viewport.zoom; // 20px padding in screen space - - this.worldX = (this.canvas.width / 2) - (menuWidthInWorld / 2); - this.worldY = this.canvas.height + paddingInWorld; - // Auto-hide mask logic this.maskWasVisible = this.canvas.maskTool.isOverlayVisible; if (this.maskWasVisible) { @@ -165,17 +158,32 @@ export class BatchPreviewManager { log.info(`Showing batch preview for ${layers.length} layers.`); this.layers = layers; this.currentIndex = 0; + + // Make the element visible BEFORE calculating its size this.element.style.display = 'flex'; this.active = true; + + // Now that it's visible, we can get its dimensions and adjust the position. + const menuWidthInWorld = this.element.offsetWidth / this.canvas.viewport.zoom; + const paddingInWorld = 20 / this.canvas.viewport.zoom; + + this.worldX -= menuWidthInWorld / 2; // Center horizontally + this.worldY += paddingInWorld; // Add padding below the output area + this._update(); } hide() { log.info('Hiding batch preview.'); - this.element.style.display = 'none'; + if (this.element) { + this.element.remove(); + } this.active = false; - this.layers = []; - this.currentIndex = 0; + + const index = this.canvas.batchPreviewManagers.indexOf(this); + if (index > -1) { + this.canvas.batchPreviewManagers.splice(index, 1); + } // Restore mask visibility if it was hidden by this manager if (this.maskWasVisible && !this.canvas.maskTool.isOverlayVisible) { diff --git a/js/Canvas.js b/js/Canvas.js index 20bd719..e0c39c1 100644 --- a/js/Canvas.js +++ b/js/Canvas.js @@ -167,7 +167,8 @@ export class Canvas { this.canvasRenderer = new CanvasRenderer(this); this.canvasIO = new CanvasIO(this); this.imageReferenceManager = new ImageReferenceManager(this); - this.batchPreviewManager = new BatchPreviewManager(this); + this.batchPreviewManagers = []; + this.pendingBatchSpawnPosition = null; log.debug('Canvas modules initialized successfully'); } @@ -485,7 +486,12 @@ export class Canvas { const handleExecutionStart = () => { lastExecutionStartTime = Date.now(); - log.debug(`Execution started, timestamp set to: ${lastExecutionStartTime}`); + // Store the spawn position for the next batch menu, relative to the output area + this.pendingBatchSpawnPosition = { + x: this.width / 2, // Horizontally centered on the output area + y: this.height // At the bottom of the output area + }; + log.debug(`Execution started, pending spawn position set relative to output area at:`, this.pendingBatchSpawnPosition); }; const handleExecutionSuccess = async () => { @@ -494,7 +500,20 @@ export class Canvas { const newLayers = await this.canvasIO.importLatestImages(lastExecutionStartTime); if (newLayers && newLayers.length > 1) { - this.batchPreviewManager.show(newLayers); + if (!this.pendingBatchSpawnPosition) { + // Fallback in case execution_start didn't fire + this.pendingBatchSpawnPosition = { + x: this.width / 2, + y: this.height + }; + log.warn("execution_start did not fire, using fallback spawn position."); + } + + const newManager = new BatchPreviewManager(this, this.pendingBatchSpawnPosition); + this.batchPreviewManagers.push(newManager); + newManager.show(newLayers); + + this.pendingBatchSpawnPosition = null; // Consume the position } } }; diff --git a/js/CanvasInteractions.js b/js/CanvasInteractions.js index 15885cf..cf19f7e 100644 --- a/js/CanvasInteractions.js +++ b/js/CanvasInteractions.js @@ -532,6 +532,14 @@ export class CanvasInteractions { this.canvas.maskTool.updatePosition(-finalX, -finalY); + // Also move any active batch preview menus + if (this.canvas.batchPreviewManagers && this.canvas.batchPreviewManagers.length > 0) { + this.canvas.batchPreviewManagers.forEach(manager => { + manager.worldX -= finalX; + manager.worldY -= finalY; + }); + } + this.canvas.viewport.x -= finalX; this.canvas.viewport.y -= finalY; } diff --git a/js/CanvasRenderer.js b/js/CanvasRenderer.js index 237a25b..e60bdcd 100644 --- a/js/CanvasRenderer.js +++ b/js/CanvasRenderer.js @@ -113,9 +113,11 @@ export class CanvasRenderer { } this.canvas.ctx.drawImage(this.canvas.offscreenCanvas, 0, 0); - // Update Batch Preview UI position - if (this.canvas.batchPreviewManager) { - this.canvas.batchPreviewManager.updateScreenPosition(this.canvas.viewport); + // Update Batch Preview UI positions + if (this.canvas.batchPreviewManagers && this.canvas.batchPreviewManagers.length > 0) { + this.canvas.batchPreviewManagers.forEach(manager => { + manager.updateScreenPosition(this.canvas.viewport); + }); } }