project migration to typescript

Project migration to typescript
This commit is contained in:
Dariusz L
2025-07-04 04:22:51 +02:00
parent 3e4cdf10bc
commit 5adc77471f
60 changed files with 12565 additions and 3021 deletions

View File

@@ -1,7 +1,5 @@
import {createModuleLogger} from "./utils/LoggerUtils.js";
import { createModuleLogger } from "./utils/LoggerUtils.js";
const log = createModuleLogger('BatchPreviewManager');
export class BatchPreviewManager {
constructor(canvas, initialPosition = { x: 0, y: 0 }, generationArea = null) {
this.canvas = canvas;
@@ -9,33 +7,25 @@ export class BatchPreviewManager {
this.layers = [];
this.currentIndex = 0;
this.element = null;
this.counterElement = null;
this.uiInitialized = false;
this.maskWasVisible = false;
// Position in canvas world coordinates
this.worldX = initialPosition.x;
this.worldY = initialPosition.y;
this.isDragging = false;
this.generationArea = generationArea; // Store the generation area
this.generationArea = generationArea;
}
updateScreenPosition(viewport) {
if (!this.active || !this.element) return;
// Translate world coordinates to screen coordinates
if (!this.active || !this.element)
return;
const screenX = (this.worldX - viewport.x) * viewport.zoom;
const screenY = (this.worldY - viewport.y) * viewport.zoom;
// We can also scale the menu with zoom, but let's keep it constant for now for readability
const scale = 1; // viewport.zoom;
// Use transform for performance
const scale = 1;
this.element.style.transform = `translate(${screenX}px, ${screenY}px) scale(${scale})`;
}
_createUI() {
if (this.uiInitialized) return;
if (this.uiInitialized)
return;
this.element = document.createElement('div');
this.element.id = 'layerforge-batch-preview';
this.element.style.cssText = `
@@ -56,65 +46,53 @@ export class BatchPreviewManager {
cursor: move;
user-select: none;
`;
this.element.addEventListener('mousedown', (e) => {
if (e.target.tagName === 'BUTTON') return;
if (e.target.tagName === 'BUTTON')
return;
e.preventDefault();
e.stopPropagation();
this.isDragging = true;
const handleMouseMove = (moveEvent) => {
if (this.isDragging) {
// Convert screen pixel movement to world coordinate movement
const deltaX = moveEvent.movementX / this.canvas.viewport.zoom;
const deltaY = moveEvent.movementY / this.canvas.viewport.zoom;
this.worldX += deltaX;
this.worldY += deltaY;
// The render loop will handle updating the screen position, but we need to trigger it.
this.canvas.render();
}
};
const handleMouseUp = () => {
this.isDragging = false;
document.removeEventListener('mousemove', handleMouseMove);
document.removeEventListener('mouseup', handleMouseUp);
};
document.addEventListener('mousemove', handleMouseMove);
document.addEventListener('mouseup', handleMouseUp);
});
const prevButton = this._createButton('◀', 'Previous'); // Left arrow
const nextButton = this._createButton('▶', 'Next'); // Right arrow
const confirmButton = this._createButton('✔', 'Confirm'); // Checkmark
const cancelButton = this._createButton('✖', 'Cancel All'); // X mark
const closeButton = this._createButton('➲', 'Close'); // Door icon
const cancelButton = this._createButton('✖', 'Cancel All');
const closeButton = this._createButton('➲', 'Close');
this.counterElement = document.createElement('span');
this.counterElement.style.minWidth = '40px';
this.counterElement.style.textAlign = 'center';
this.counterElement.style.fontWeight = 'bold';
prevButton.onclick = () => this.navigate(-1);
nextButton.onclick = () => this.navigate(1);
confirmButton.onclick = () => this.confirm();
cancelButton.onclick = () => this.cancelAndRemoveAll();
closeButton.onclick = () => this.hide();
this.element.append(prevButton, this.counterElement, nextButton, confirmButton, cancelButton, closeButton);
if (this.canvas.canvas.parentNode) {
this.canvas.canvas.parentNode.appendChild(this.element);
} else {
if (this.canvas.canvas.parentElement) {
this.canvas.canvas.parentElement.appendChild(this.element);
}
else {
log.error("Could not find parent node to attach batch preview UI.");
}
this.uiInitialized = true;
}
_createButton(innerHTML, title) {
const button = document.createElement('button');
button.innerHTML = innerHTML;
@@ -136,14 +114,11 @@ export class BatchPreviewManager {
button.onmouseout = () => button.style.background = '#555';
return button;
}
show(layers) {
if (!layers || layers.length <= 1) {
return;
}
this._createUI();
// Auto-hide mask logic
this.maskWasVisible = this.canvas.maskTool.isOverlayVisible;
if (this.maskWasVisible) {
@@ -155,103 +130,83 @@ export class BatchPreviewManager {
}
this.canvas.render();
}
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';
if (this.element) {
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
if (this.element) {
const menuWidthInWorld = this.element.offsetWidth / this.canvas.viewport.zoom;
const paddingInWorld = 20 / this.canvas.viewport.zoom;
this.worldX -= menuWidthInWorld / 2;
this.worldY += paddingInWorld;
}
this._update();
}
hide() {
log.info('Hiding batch preview.');
if (this.element) {
this.element.remove();
}
this.active = false;
const index = this.canvas.batchPreviewManagers.indexOf(this);
if (index > -1) {
this.canvas.batchPreviewManagers.splice(index, 1);
}
// Trigger a final render to ensure the generation area outline is removed
this.canvas.render();
// Restore mask visibility if it was hidden by this manager
if (this.maskWasVisible && !this.canvas.maskTool.isOverlayVisible) {
this.canvas.maskTool.toggleOverlayVisibility();
const toggleBtn = document.getElementById(`toggle-mask-btn-${this.canvas.node.id}`);
const toggleBtn = document.getElementById(`toggle-mask-btn-${String(this.canvas.node.id)}`);
if (toggleBtn) {
toggleBtn.classList.add('primary');
toggleBtn.textContent = "Show Mask";
}
}
this.maskWasVisible = false; // Reset state
// Make all layers visible again upon closing
this.canvas.layers.forEach(l => l.visible = true);
this.maskWasVisible = false;
this.canvas.layers.forEach((l) => l.visible = true);
this.canvas.render();
}
navigate(direction) {
this.currentIndex += direction;
if (this.currentIndex < 0) {
this.currentIndex = this.layers.length - 1;
} else if (this.currentIndex >= this.layers.length) {
}
else if (this.currentIndex >= this.layers.length) {
this.currentIndex = 0;
}
this._update();
}
confirm() {
const layerToKeep = this.layers[this.currentIndex];
log.info(`Confirming selection: Keeping layer ${layerToKeep.id}.`);
const layersToDelete = this.layers.filter(l => l.id !== layerToKeep.id);
const layerIdsToDelete = layersToDelete.map(l => l.id);
const layersToDelete = this.layers.filter((l) => l.id !== layerToKeep.id);
const layerIdsToDelete = layersToDelete.map((l) => l.id);
this.canvas.removeLayersByIds(layerIdsToDelete);
log.info(`Deleted ${layersToDelete.length} other layers.`);
this.hide();
}
cancelAndRemoveAll() {
log.info('Cancel clicked. Removing all new layers.');
const layerIdsToDelete = this.layers.map(l => l.id);
const layerIdsToDelete = this.layers.map((l) => l.id);
this.canvas.removeLayersByIds(layerIdsToDelete);
log.info(`Deleted all ${layerIdsToDelete.length} new layers.`);
this.hide();
}
_update() {
this.counterElement.textContent = `${this.currentIndex + 1} / ${this.layers.length}`;
if (this.counterElement) {
this.counterElement.textContent = `${this.currentIndex + 1} / ${this.layers.length}`;
}
this._focusOnLayer(this.layers[this.currentIndex]);
}
_focusOnLayer(layer) {
if (!layer) return;
if (!layer)
return;
log.debug(`Focusing on layer ${layer.id}`);
// Move the selected layer to the top of the layer stack
this.canvas.canvasLayers.moveLayers([layer], { toIndex: 0 });
this.canvas.updateSelection([layer]);
// Render is called by moveLayers, but we call it again to be safe
this.canvas.render();
}