mirror of
https://github.com/Azornes/Comfyui-LayerForge.git
synced 2026-03-21 20:52:12 -03:00
project migration to typescript
Project migration to typescript
This commit is contained in:
238
js/Canvas.js
238
js/Canvas.js
@@ -1,33 +1,29 @@
|
||||
import {app, ComfyApp} from "../../scripts/app.js";
|
||||
import {api} from "../../scripts/api.js";
|
||||
import {removeImage} from "./db.js";
|
||||
import {MaskTool} from "./MaskTool.js";
|
||||
import {CanvasState} from "./CanvasState.js";
|
||||
import {CanvasInteractions} from "./CanvasInteractions.js";
|
||||
import {CanvasLayers} from "./CanvasLayers.js";
|
||||
import {CanvasLayersPanel} from "./CanvasLayersPanel.js";
|
||||
import {CanvasRenderer} from "./CanvasRenderer.js";
|
||||
import {CanvasIO} from "./CanvasIO.js";
|
||||
import {ImageReferenceManager} from "./ImageReferenceManager.js";
|
||||
import {BatchPreviewManager} from "./BatchPreviewManager.js";
|
||||
import {createModuleLogger} from "./utils/LoggerUtils.js";
|
||||
// @ts-ignore
|
||||
import { api } from "../../scripts/api.js";
|
||||
import { MaskTool } from "./MaskTool.js";
|
||||
import { CanvasState } from "./CanvasState.js";
|
||||
import { CanvasInteractions } from "./CanvasInteractions.js";
|
||||
import { CanvasLayers } from "./CanvasLayers.js";
|
||||
import { CanvasLayersPanel } from "./CanvasLayersPanel.js";
|
||||
import { CanvasRenderer } from "./CanvasRenderer.js";
|
||||
import { CanvasIO } from "./CanvasIO.js";
|
||||
import { ImageReferenceManager } from "./ImageReferenceManager.js";
|
||||
import { BatchPreviewManager } from "./BatchPreviewManager.js";
|
||||
import { createModuleLogger } from "./utils/LoggerUtils.js";
|
||||
import { debounce } from "./utils/CommonUtils.js";
|
||||
import {CanvasMask} from "./CanvasMask.js";
|
||||
import {CanvasSelection} from "./CanvasSelection.js";
|
||||
|
||||
import { CanvasMask } from "./CanvasMask.js";
|
||||
import { CanvasSelection } from "./CanvasSelection.js";
|
||||
const useChainCallback = (original, next) => {
|
||||
if (original === undefined || original === null) {
|
||||
return next;
|
||||
}
|
||||
return function(...args) {
|
||||
const originalReturn = original.apply(this, args);
|
||||
const nextReturn = next.apply(this, args);
|
||||
return nextReturn === undefined ? originalReturn : nextReturn;
|
||||
};
|
||||
if (original === undefined || original === null) {
|
||||
return next;
|
||||
}
|
||||
return function (...args) {
|
||||
const originalReturn = original.apply(this, args);
|
||||
const nextReturn = next.apply(this, args);
|
||||
return nextReturn === undefined ? originalReturn : nextReturn;
|
||||
};
|
||||
};
|
||||
|
||||
const log = createModuleLogger('Canvas');
|
||||
|
||||
/**
|
||||
* Canvas - Fasada dla systemu rysowania
|
||||
*
|
||||
@@ -41,65 +37,72 @@ export class Canvas {
|
||||
this.node = node;
|
||||
this.widget = widget;
|
||||
this.canvas = document.createElement('canvas');
|
||||
this.ctx = this.canvas.getContext('2d', {willReadFrequently: true});
|
||||
const ctx = this.canvas.getContext('2d', { willReadFrequently: true });
|
||||
if (!ctx)
|
||||
throw new Error("Could not create canvas context");
|
||||
this.ctx = ctx;
|
||||
this.width = 512;
|
||||
this.height = 512;
|
||||
this.layers = [];
|
||||
this.onStateChange = callbacks.onStateChange || null;
|
||||
this.lastMousePosition = {x: 0, y: 0};
|
||||
|
||||
this.onStateChange = callbacks.onStateChange;
|
||||
this.onHistoryChange = callbacks.onHistoryChange;
|
||||
this.lastMousePosition = { x: 0, y: 0 };
|
||||
this.viewport = {
|
||||
x: -(this.width / 4),
|
||||
y: -(this.height / 4),
|
||||
zoom: 0.8,
|
||||
};
|
||||
|
||||
this.offscreenCanvas = document.createElement('canvas');
|
||||
this.offscreenCtx = this.offscreenCanvas.getContext('2d', {
|
||||
alpha: false
|
||||
});
|
||||
|
||||
this.dataInitialized = false;
|
||||
this.pendingDataCheck = null;
|
||||
this.imageCache = new Map();
|
||||
|
||||
this._initializeModules(callbacks);
|
||||
|
||||
this._setupCanvas();
|
||||
|
||||
this.requestSaveState = () => { };
|
||||
this.maskTool = new MaskTool(this, { onStateChange: this.onStateChange });
|
||||
this.canvasMask = new CanvasMask(this);
|
||||
this.canvasState = new CanvasState(this);
|
||||
this.canvasSelection = new CanvasSelection(this);
|
||||
this.canvasInteractions = new CanvasInteractions(this);
|
||||
this.canvasLayers = new CanvasLayers(this);
|
||||
this.canvasLayersPanel = new CanvasLayersPanel(this);
|
||||
this.canvasRenderer = new CanvasRenderer(this);
|
||||
this.canvasIO = new CanvasIO(this);
|
||||
this.imageReferenceManager = new ImageReferenceManager(this);
|
||||
this.batchPreviewManagers = [];
|
||||
this.pendingBatchContext = null;
|
||||
this.interaction = this.canvasInteractions.interaction;
|
||||
|
||||
this.previewVisible = false;
|
||||
this.isMouseOver = false;
|
||||
this._initializeModules();
|
||||
this._setupCanvas();
|
||||
log.debug('Canvas widget element:', this.node);
|
||||
log.info('Canvas initialized', {
|
||||
nodeId: this.node.id,
|
||||
dimensions: {width: this.width, height: this.height},
|
||||
dimensions: { width: this.width, height: this.height },
|
||||
viewport: this.viewport
|
||||
});
|
||||
|
||||
this.setPreviewVisibility(false);
|
||||
}
|
||||
|
||||
|
||||
async waitForWidget(name, node, interval = 100, timeout = 20000) {
|
||||
const startTime = Date.now();
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const check = () => {
|
||||
const widget = node.widgets.find(w => w.name === name);
|
||||
const widget = node.widgets.find((w) => w.name === name);
|
||||
if (widget) {
|
||||
resolve(widget);
|
||||
} else if (Date.now() - startTime > timeout) {
|
||||
}
|
||||
else if (Date.now() - startTime > timeout) {
|
||||
reject(new Error(`Widget "${name}" not found within timeout.`));
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
setTimeout(check, interval);
|
||||
}
|
||||
};
|
||||
|
||||
check();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Kontroluje widoczność podglądu canvas
|
||||
* @param {boolean} visible - Czy podgląd ma być widoczny
|
||||
@@ -107,11 +110,9 @@ export class Canvas {
|
||||
async setPreviewVisibility(visible) {
|
||||
this.previewVisible = visible;
|
||||
log.info("Canvas preview visibility set to:", visible);
|
||||
|
||||
const imagePreviewWidget = await this.waitForWidget("$$canvas-image-preview", this.node);
|
||||
if (imagePreviewWidget) {
|
||||
log.debug("Found $$canvas-image-preview widget, controlling visibility");
|
||||
|
||||
if (visible) {
|
||||
if (imagePreviewWidget.options) {
|
||||
imagePreviewWidget.options.hidden = false;
|
||||
@@ -125,7 +126,8 @@ export class Canvas {
|
||||
imagePreviewWidget.computeSize = function () {
|
||||
return [0, 250]; // Szerokość 0 (auto), wysokość 250
|
||||
};
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
if (imagePreviewWidget.options) {
|
||||
imagePreviewWidget.options.hidden = true;
|
||||
}
|
||||
@@ -135,44 +137,27 @@ export class Canvas {
|
||||
if ('hidden' in imagePreviewWidget) {
|
||||
imagePreviewWidget.hidden = true;
|
||||
}
|
||||
|
||||
imagePreviewWidget.computeSize = function () {
|
||||
return [0, 0]; // Szerokość 0, wysokość 0
|
||||
};
|
||||
}
|
||||
this.render()
|
||||
} else {
|
||||
this.render();
|
||||
}
|
||||
else {
|
||||
log.warn("$$canvas-image-preview widget not found in Canvas.js");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inicjalizuje moduły systemu canvas
|
||||
* @private
|
||||
*/
|
||||
_initializeModules(callbacks) {
|
||||
_initializeModules() {
|
||||
log.debug('Initializing Canvas modules...');
|
||||
|
||||
// Stwórz opóźnioną wersję funkcji zapisu stanu
|
||||
this.requestSaveState = debounce(this.saveState.bind(this), 500);
|
||||
|
||||
this.requestSaveState = debounce(() => this.saveState(), 500);
|
||||
this._addAutoRefreshToggle();
|
||||
this.maskTool = new MaskTool(this, {onStateChange: this.onStateChange});
|
||||
this.canvasMask = new CanvasMask(this);
|
||||
this.canvasState = new CanvasState(this);
|
||||
this.canvasSelection = new CanvasSelection(this);
|
||||
this.canvasInteractions = new CanvasInteractions(this);
|
||||
this.canvasLayers = new CanvasLayers(this);
|
||||
this.canvasLayersPanel = new CanvasLayersPanel(this);
|
||||
this.canvasRenderer = new CanvasRenderer(this);
|
||||
this.canvasIO = new CanvasIO(this);
|
||||
this.imageReferenceManager = new ImageReferenceManager(this);
|
||||
this.batchPreviewManagers = [];
|
||||
this.pendingBatchContext = null;
|
||||
|
||||
log.debug('Canvas modules initialized successfully');
|
||||
}
|
||||
|
||||
/**
|
||||
* Konfiguruje podstawowe właściwości canvas
|
||||
* @private
|
||||
@@ -181,14 +166,11 @@ export class Canvas {
|
||||
this.initCanvas();
|
||||
this.canvasInteractions.setupEventListeners();
|
||||
this.canvasIO.initNodeData();
|
||||
|
||||
this.layers = this.layers.map(layer => ({
|
||||
this.layers = this.layers.map((layer) => ({
|
||||
...layer,
|
||||
opacity: 1
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Ładuje stan canvas z bazy danych
|
||||
*/
|
||||
@@ -201,24 +183,21 @@ export class Canvas {
|
||||
}
|
||||
this.saveState();
|
||||
this.render();
|
||||
|
||||
// Dodaj to wywołanie, aby panel renderował się po załadowaniu stanu
|
||||
if (this.canvasLayersPanel) {
|
||||
this.canvasLayersPanel.onLayersChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Zapisuje obecny stan
|
||||
* @param {boolean} replaceLast - Czy zastąpić ostatni stan w historii
|
||||
*/
|
||||
saveState(replaceLast = false) {
|
||||
log.debug('Saving canvas state', {replaceLast, layersCount: this.layers.length});
|
||||
log.debug('Saving canvas state', { replaceLast, layersCount: this.layers.length });
|
||||
this.canvasState.saveState(replaceLast);
|
||||
this.incrementOperationCount();
|
||||
this._notifyStateChange();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cofnij ostatnią operację
|
||||
*/
|
||||
@@ -226,21 +205,16 @@ export class Canvas {
|
||||
log.info('Performing undo operation');
|
||||
const historyInfo = this.canvasState.getHistoryInfo();
|
||||
log.debug('History state before undo:', historyInfo);
|
||||
|
||||
this.canvasState.undo();
|
||||
this.incrementOperationCount();
|
||||
this._notifyStateChange();
|
||||
|
||||
// Powiadom panel warstw o zmianie stanu warstw
|
||||
if (this.canvasLayersPanel) {
|
||||
this.canvasLayersPanel.onLayersChanged();
|
||||
this.canvasLayersPanel.onSelectionChanged();
|
||||
}
|
||||
|
||||
log.debug('Undo completed, layers count:', this.layers.length);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Ponów cofniętą operację
|
||||
*/
|
||||
@@ -248,27 +222,22 @@ export class Canvas {
|
||||
log.info('Performing redo operation');
|
||||
const historyInfo = this.canvasState.getHistoryInfo();
|
||||
log.debug('History state before redo:', historyInfo);
|
||||
|
||||
this.canvasState.redo();
|
||||
this.incrementOperationCount();
|
||||
this._notifyStateChange();
|
||||
|
||||
// Powiadom panel warstw o zmianie stanu warstw
|
||||
if (this.canvasLayersPanel) {
|
||||
this.canvasLayersPanel.onLayersChanged();
|
||||
this.canvasLayersPanel.onSelectionChanged();
|
||||
}
|
||||
|
||||
log.debug('Redo completed, layers count:', this.layers.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renderuje canvas
|
||||
*/
|
||||
render() {
|
||||
this.canvasRenderer.render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Dodaje warstwę z obrazem
|
||||
* @param {Image} image - Obraz do dodania
|
||||
@@ -277,49 +246,40 @@ export class Canvas {
|
||||
*/
|
||||
async addLayer(image, layerProps = {}, addMode = 'default') {
|
||||
const result = await this.canvasLayers.addLayerWithImage(image, layerProps, addMode);
|
||||
|
||||
// Powiadom panel warstw o dodaniu nowej warstwy
|
||||
if (this.canvasLayersPanel) {
|
||||
this.canvasLayersPanel.onLayersChanged();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Usuwa wybrane warstwy
|
||||
*/
|
||||
removeLayersByIds(layerIds) {
|
||||
if (!layerIds || layerIds.length === 0) return;
|
||||
|
||||
if (!layerIds || layerIds.length === 0)
|
||||
return;
|
||||
const initialCount = this.layers.length;
|
||||
this.saveState();
|
||||
this.layers = this.layers.filter(l => !layerIds.includes(l.id));
|
||||
|
||||
this.layers = this.layers.filter((l) => !layerIds.includes(l.id));
|
||||
// If the current selection was part of the removal, clear it
|
||||
const newSelection = this.canvasSelection.selectedLayers.filter(l => !layerIds.includes(l.id));
|
||||
const newSelection = this.canvasSelection.selectedLayers.filter((l) => !layerIds.includes(l.id));
|
||||
this.canvasSelection.updateSelection(newSelection);
|
||||
|
||||
this.render();
|
||||
this.saveState();
|
||||
|
||||
if (this.canvasLayersPanel) {
|
||||
this.canvasLayersPanel.onLayersChanged();
|
||||
}
|
||||
log.info(`Removed ${initialCount - this.layers.length} layers by ID.`);
|
||||
}
|
||||
|
||||
removeSelectedLayers() {
|
||||
return this.canvasSelection.removeSelectedLayers();
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplikuje zaznaczone warstwy (w pamięci, bez zapisu stanu)
|
||||
*/
|
||||
duplicateSelectedLayers() {
|
||||
return this.canvasSelection.duplicateSelectedLayers();
|
||||
}
|
||||
|
||||
/**
|
||||
* Aktualizuje zaznaczenie warstw i powiadamia wszystkie komponenty.
|
||||
* To jest "jedyne źródło prawdy" o zmianie zaznaczenia.
|
||||
@@ -328,14 +288,12 @@ export class Canvas {
|
||||
updateSelection(newSelection) {
|
||||
return this.canvasSelection.updateSelection(newSelection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logika aktualizacji zaznaczenia, wywoływana przez panel warstw.
|
||||
*/
|
||||
updateSelectionLogic(layer, isCtrlPressed, isShiftPressed, index) {
|
||||
return this.canvasSelection.updateSelectionLogic(layer, isCtrlPressed, isShiftPressed, index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Zmienia rozmiar obszaru wyjściowego
|
||||
* @param {number} width - Nowa szerokość
|
||||
@@ -345,32 +303,27 @@ export class Canvas {
|
||||
updateOutputAreaSize(width, height, saveHistory = true) {
|
||||
return this.canvasLayers.updateOutputAreaSize(width, height, saveHistory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Eksportuje spłaszczony canvas jako blob
|
||||
*/
|
||||
async getFlattenedCanvasAsBlob() {
|
||||
return this.canvasLayers.getFlattenedCanvasAsBlob();
|
||||
}
|
||||
|
||||
/**
|
||||
* Eksportuje spłaszczony canvas z maską jako kanałem alpha
|
||||
*/
|
||||
async getFlattenedCanvasWithMaskAsBlob() {
|
||||
return this.canvasLayers.getFlattenedCanvasWithMaskAsBlob();
|
||||
}
|
||||
|
||||
/**
|
||||
* Importuje najnowszy obraz
|
||||
*/
|
||||
async importLatestImage() {
|
||||
return this.canvasIO.importLatestImage();
|
||||
}
|
||||
|
||||
_addAutoRefreshToggle() {
|
||||
let autoRefreshEnabled = false;
|
||||
let lastExecutionStartTime = 0;
|
||||
|
||||
const handleExecutionStart = () => {
|
||||
if (autoRefreshEnabled) {
|
||||
lastExecutionStartTime = Date.now();
|
||||
@@ -393,62 +346,40 @@ export class Canvas {
|
||||
this.render(); // Trigger render to show the pending outline immediately
|
||||
}
|
||||
};
|
||||
|
||||
const handleExecutionSuccess = async () => {
|
||||
if (autoRefreshEnabled) {
|
||||
log.info('Auto-refresh triggered, importing latest images.');
|
||||
|
||||
if (!this.pendingBatchContext) {
|
||||
log.warn("execution_start did not fire, cannot process batch. Awaiting next execution.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Use the captured output area for image import
|
||||
const newLayers = await this.canvasIO.importLatestImages(
|
||||
lastExecutionStartTime,
|
||||
this.pendingBatchContext.outputArea
|
||||
);
|
||||
|
||||
const newLayers = await this.canvasIO.importLatestImages(lastExecutionStartTime, this.pendingBatchContext.outputArea);
|
||||
if (newLayers && newLayers.length > 1) {
|
||||
const newManager = new BatchPreviewManager(
|
||||
this,
|
||||
this.pendingBatchContext.spawnPosition,
|
||||
this.pendingBatchContext.outputArea
|
||||
);
|
||||
const newManager = new BatchPreviewManager(this, this.pendingBatchContext.spawnPosition, this.pendingBatchContext.outputArea);
|
||||
this.batchPreviewManagers.push(newManager);
|
||||
newManager.show(newLayers);
|
||||
}
|
||||
|
||||
// Consume the context
|
||||
this.pendingBatchContext = null;
|
||||
// Final render to clear the outline if it was the last one
|
||||
this.render();
|
||||
}
|
||||
};
|
||||
|
||||
this.node.addWidget(
|
||||
'toggle',
|
||||
'Auto-refresh after generation',
|
||||
false,
|
||||
(value) => {
|
||||
autoRefreshEnabled = value;
|
||||
log.debug('Auto-refresh toggled:', value);
|
||||
}, {
|
||||
serialize: false
|
||||
}
|
||||
);
|
||||
|
||||
this.node.addWidget('toggle', 'Auto-refresh after generation', false, (value) => {
|
||||
autoRefreshEnabled = value;
|
||||
log.debug('Auto-refresh toggled:', value);
|
||||
}, {
|
||||
serialize: false
|
||||
});
|
||||
api.addEventListener('execution_start', handleExecutionStart);
|
||||
api.addEventListener('execution_success', handleExecutionSuccess);
|
||||
|
||||
this.node.onRemoved = useChainCallback(this.node.onRemoved, () => {
|
||||
log.info('Node removed, cleaning up auto-refresh listeners.');
|
||||
api.removeEventListener('execution_start', handleExecutionStart);
|
||||
api.removeEventListener('execution_success', handleExecutionSuccess);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Uruchamia edytor masek
|
||||
* @param {Image|HTMLCanvasElement|null} predefinedMask - Opcjonalna maska do nałożenia po otwarciu editora
|
||||
@@ -457,8 +388,6 @@ export class Canvas {
|
||||
async startMaskEditor(predefinedMask = null, sendCleanImage = true) {
|
||||
return this.canvasMask.startMaskEditor(predefinedMask, sendCleanImage);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Inicjalizuje podstawowe właściwości canvas
|
||||
*/
|
||||
@@ -473,29 +402,24 @@ export class Canvas {
|
||||
this.canvas.tabIndex = 0;
|
||||
this.canvas.style.outline = 'none';
|
||||
}
|
||||
|
||||
/**
|
||||
* Pobiera współrzędne myszy w układzie świata
|
||||
* @param {MouseEvent} e - Zdarzenie myszy
|
||||
*/
|
||||
getMouseWorldCoordinates(e) {
|
||||
const rect = this.canvas.getBoundingClientRect();
|
||||
|
||||
const mouseX_DOM = e.clientX - rect.left;
|
||||
const mouseY_DOM = e.clientY - rect.top;
|
||||
|
||||
if (!this.offscreenCanvas)
|
||||
throw new Error("Offscreen canvas not initialized");
|
||||
const scaleX = this.offscreenCanvas.width / rect.width;
|
||||
const scaleY = this.offscreenCanvas.height / rect.height;
|
||||
|
||||
const mouseX_Buffer = mouseX_DOM * scaleX;
|
||||
const mouseY_Buffer = mouseY_DOM * scaleY;
|
||||
|
||||
const worldX = (mouseX_Buffer / this.viewport.zoom) + this.viewport.x;
|
||||
const worldY = (mouseY_Buffer / this.viewport.zoom) + this.viewport.y;
|
||||
|
||||
return {x: worldX, y: worldY};
|
||||
return { x: worldX, y: worldY };
|
||||
}
|
||||
|
||||
/**
|
||||
* Pobiera współrzędne myszy w układzie widoku
|
||||
* @param {MouseEvent} e - Zdarzenie myszy
|
||||
@@ -504,23 +428,18 @@ export class Canvas {
|
||||
const rect = this.canvas.getBoundingClientRect();
|
||||
const mouseX_DOM = e.clientX - rect.left;
|
||||
const mouseY_DOM = e.clientY - rect.top;
|
||||
|
||||
const scaleX = this.canvas.width / rect.width;
|
||||
const scaleY = this.canvas.height / rect.height;
|
||||
|
||||
const mouseX_Canvas = mouseX_DOM * scaleX;
|
||||
const mouseY_Canvas = mouseY_DOM * scaleY;
|
||||
|
||||
return {x: mouseX_Canvas, y: mouseY_Canvas};
|
||||
return { x: mouseX_Canvas, y: mouseY_Canvas };
|
||||
}
|
||||
|
||||
/**
|
||||
* Aktualizuje zaznaczenie po operacji historii
|
||||
*/
|
||||
updateSelectionAfterHistory() {
|
||||
return this.canvasSelection.updateSelectionAfterHistory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Aktualizuje przyciski historii
|
||||
*/
|
||||
@@ -533,7 +452,6 @@ export class Canvas {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Zwiększa licznik operacji (dla garbage collection)
|
||||
*/
|
||||
@@ -542,7 +460,6 @@ export class Canvas {
|
||||
this.imageReferenceManager.incrementOperationCount();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Czyści zasoby canvas
|
||||
*/
|
||||
@@ -552,7 +469,6 @@ export class Canvas {
|
||||
}
|
||||
log.info("Canvas destroyed");
|
||||
}
|
||||
|
||||
/**
|
||||
* Powiadamia o zmianie stanu
|
||||
* @private
|
||||
|
||||
Reference in New Issue
Block a user