From c023aa750214d7131c7231d35a973c65e91890bd Mon Sep 17 00:00:00 2001 From: disty Date: Thu, 5 Dec 2024 16:03:35 +0200 Subject: [PATCH] add improved canvas navigation --- __init__.py | 2 +- pyproject.toml | 2 +- web/core/css/main.css | 3 + .../js/common/components/CanvasComponent.js | 6 +- web/core/js/common/components/Seeder.js | 2 +- .../components/canvas/CanvasControlsPlugin.js | 498 ++++++++++++------ .../common/components/canvas/CanvasLoader.js | 125 +++-- .../components/canvas/CustomBrushPlugin.js | 455 +++++++++++----- .../components/canvas/ImageLoaderPlugin.js | 343 ++++++------ .../components/canvas/MaskBrushPlugin.js | 55 +- .../components/canvas/MaskExportUtilities.js | 5 +- .../js/common/components/messageHandler.js | 304 ++++++----- .../js/common/scripts/stateManagerMain.js | 20 +- 13 files changed, 1093 insertions(+), 727 deletions(-) diff --git a/__init__.py b/__init__.py index 128ad3a..d7036cf 100644 --- a/__init__.py +++ b/__init__.py @@ -38,7 +38,7 @@ NODE_DISPLAY_NAME_MAPPINGS: Dict[str, str] = {} APP_CONFIGS: List[AppConfig] = [] APP_NAME: str = "Flow" -APP_VERSION: str = "0.4.1" +APP_VERSION: str = "0.4.2" PURPLE = "\033[38;5;129m" RESET = "\033[0m" FLOWMSG = f"{PURPLE}Flow{RESET}" diff --git a/pyproject.toml b/pyproject.toml index 4be9ed4..ff9d3c2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "comfyui-disty-flow" description = "Flow is a custom node designed to provide a more user-friendly interface for ComfyUI by acting as an alternative user interface for running workflows. It is not a replacement for workflow creation.\nFlow is currently in the early stages of development, so expect bugs and ongoing feature enhancements. With your support and feedback, Flow will settle into a steady stream." -version = "0.4.1" +version = "0.4.2" license = {file = "LICENSE"} [project.urls] diff --git a/web/core/css/main.css b/web/core/css/main.css index d708353..2612a87 100644 --- a/web/core/css/main.css +++ b/web/core/css/main.css @@ -2188,4 +2188,7 @@ html:not(.css-loading) body { } + .upper-canvas, .lower-canvas { + pointer-events: auto !important; + } \ No newline at end of file diff --git a/web/core/js/common/components/CanvasComponent.js b/web/core/js/common/components/CanvasComponent.js index c420394..9dd5eaa 100644 --- a/web/core/js/common/components/CanvasComponent.js +++ b/web/core/js/common/components/CanvasComponent.js @@ -62,7 +62,7 @@ async function processAndUpload( let result; try { result = await response.json(); - console.log(`Server Response for ${id}:`, result); + // console.log(`Server Response for ${id}:`, result); } catch (e) { throw new Error('Invalid JSON response from server.'); } @@ -94,7 +94,7 @@ async function processAndUpload( // // console.log('Mask Image set in MessageHandler'); // break; default: - console.warn(`No setter defined for upload description: ${uploadDescription}`); + // console.warn(`No setter defined for upload description: ${uploadDescription}`); } } else { throw new Error('Server response did not include imageUrl or imageName.'); @@ -104,7 +104,7 @@ async function processAndUpload( } } catch (error) { console.error(`Error uploading ${uploadDescription.toLowerCase()} ${id}:`, error); - alert(`Error uploading ${label}: ${error.message}`); + // alert(`Error uploading ${label}: ${error.message}`); } finally { hideSpinner(); } diff --git a/web/core/js/common/components/Seeder.js b/web/core/js/common/components/Seeder.js index dac87ac..34c2a64 100644 --- a/web/core/js/common/components/Seeder.js +++ b/web/core/js/common/components/Seeder.js @@ -40,7 +40,7 @@ class Seeder { ${this.config.label}
- +
diff --git a/web/core/js/common/components/canvas/CanvasControlsPlugin.js b/web/core/js/common/components/canvas/CanvasControlsPlugin.js index f673f58..1121b02 100644 --- a/web/core/js/common/components/canvas/CanvasControlsPlugin.js +++ b/web/core/js/common/components/canvas/CanvasControlsPlugin.js @@ -15,16 +15,26 @@ export class CanvasControlsPlugin extends CanvasPlugin { this.onKeyUp = this.onKeyUp.bind(this); this.onAfterRender = this.onAfterRender.bind(this); this.arraysEqual = this.arraysEqual.bind(this); - this.onMaskActivated = this.onMaskActivated.bind(this); - this.onMaskDeactivated = this.onMaskDeactivated.bind(this); + + this.onBrushActivated = this.onBrushActivated.bind(this); + this.onBrushDeactivated = this.onBrushDeactivated.bind(this); + this.onMouseWheel = this.onMouseWheel.bind(this); + this.canvasManager = null; this.canvas = null; this.isPanning = false; - this.isPanMode = false; - this.isAltPan = false; + this.isPanMode = true; + this.isAltPan = false; this.lastPosX = 0; this.lastPosY = 0; this.lastTransform = null; + this.isDrawingMode = false; + this.isBrushActivated = false; + + this.switchViewToggleButton = options.switchViewToggleButton || false; + this.currentView = 'canvasView'; + this.maskingPreviewType = 'cropped'; + } init(canvasManager) { @@ -36,116 +46,15 @@ export class CanvasControlsPlugin extends CanvasPlugin { this.attachEventListeners(); this.lastTransform = this.canvas.viewportTransform.slice(); - } createUI() { const styleSheet = document.createElement('style'); - styleSheet.textContent = ` - .cc-container * { - box-sizing: border-box; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; - } - - .cc-container { - display: inline-flex; - gap: 0.5rem; - padding: 0.5rem; - /* background: var(--color-background); */ - /* border: 1px dashed var(--color-border); */ - user-select: none; - } - - .cc-button { - display: inline-flex; - align-items: center; - justify-content: center; - padding: 0.5rem; - background: var(--color-button-primary); - border: none; - cursor: pointer; - transition: all 0.2s; - color: var(--color-primary-text); - min-width: 36px; - height: 36px; - } - - .cc-button:hover { - background: var(--color-button-primary-hover); - } - - .cc-button svg { - width: 1.25rem; - height: 1.25rem; - } - - .cc-button[data-active="true"] { - border: 1px dashed var(--color-button-secondary-text-active); - } - - .cc-divider { - width: 1px; - border-left: 1px dashed var(--color-border); - margin: 0 0.25rem; - display: none; - } - #panBtn { - - } - `; + styleSheet.textContent = this.getCSS(); document.head.appendChild(styleSheet); const temp = document.createElement('div'); - temp.innerHTML = ` -
- - - - -
- - - - - -
- `; + temp.innerHTML = this.getHTML(); this.uiContainer = temp.firstElementChild; this.zoomInBtn = this.uiContainer.querySelector('#zoomInBtn'); @@ -153,6 +62,10 @@ export class CanvasControlsPlugin extends CanvasPlugin { this.resetZoomBtn = this.uiContainer.querySelector('#resetZoomBtn'); this.panBtn = this.uiContainer.querySelector('#panBtn'); + if (this.switchViewToggleButton) { + this.switchViewBtn = this.uiContainer.querySelector('#switchViewBtn'); + } + const pluginUIContainer = document.getElementById('pluginUIContainer'); if (pluginUIContainer) { pluginUIContainer.appendChild(this.uiContainer); @@ -165,6 +78,29 @@ export class CanvasControlsPlugin extends CanvasPlugin { this.updatePanButtonState = () => { this.panBtn.dataset.active = this.isPanMode || this.isAltPan; }; + + if (this.switchViewToggleButton && this.switchViewBtn) { + this.switchViewBtn.addEventListener('click', this.toggleView.bind(this)); + } + } + + toggleView() { + if (this.currentView === 'canvasView') { + this.currentView = 'splitView'; + this.maskingPreviewType = 'none'; + this.switchViewBtn.innerHTML = this.getCanvasViewIcon(); + } else { + this.currentView = 'canvasView'; + this.maskingPreviewType = 'cropped'; + this.switchViewBtn.innerHTML = this.getSplitViewIcon(); + } + + if (this.canvasManager && typeof this.canvasManager.emit === 'function') { + this.canvasManager.emit('switchView', this.currentView); + // this.canvasManager.emit('switchMaskingPreviewType', this.maskingPreviewType); + } else { + console.warn('canvasManager.emit is not a function. Unable to emit switchView event.'); + } } attachEventListeners() { @@ -173,17 +109,17 @@ export class CanvasControlsPlugin extends CanvasPlugin { this.resetZoomBtn.addEventListener('click', this.resetZoom); this.panBtn.addEventListener('click', this.togglePanMode); - - this.canvas.on('mouse:down', this.onMouseDown); this.canvas.on('mouse:move', this.onMouseMove); this.canvas.on('mouse:up', this.onMouseUp); this.canvas.on('mouse:out', this.onMouseUp); + this.canvas.on('mouse:wheel', this.onMouseWheel); + + this.canvasManager.on('brush:activated', this.onBrushActivated); + this.canvasManager.on('brush:deactivated', this.onBrushDeactivated); this.canvas.on('after:render', this.onAfterRender); - this.canvasManager.on('mask:activated', this.onMaskActivated); - this.canvasManager.on('mask:deactivated', this.onMaskDeactivated); document.addEventListener('keydown', this.onKeyDown); document.addEventListener('keyup', this.onKeyUp); @@ -193,7 +129,11 @@ export class CanvasControlsPlugin extends CanvasPlugin { this.zoomInBtn.removeEventListener('click', this.zoomIn); this.zoomOutBtn.removeEventListener('click', this.zoomOut); this.resetZoomBtn.removeEventListener('click', this.resetZoom); - this.panBtn.removeEventListener('click', this.togglePanMode); + this.panBtn.removeEventListener('click', this.togglePanMode); + + if (this.switchViewToggleButton && this.switchViewBtn) { + this.switchViewBtn.removeEventListener('click', this.toggleView.bind(this)); + } this.canvas.off('mouse:down', this.onMouseDown); this.canvas.off('mouse:move', this.onMouseMove); @@ -202,8 +142,10 @@ export class CanvasControlsPlugin extends CanvasPlugin { this.canvas.off('after:render', this.onAfterRender); - this.canvasManager.off('mask:activated', this.onMaskActivated); - this.canvasManager.off('mask:deactivated', this.onMaskDeactivated); + this.canvasManager.on('brush:activated', this.onBrushActivated); + this.canvasManager.on('brush:deactivated', this.onBrushDeactivated); + + this.canvas.off('mouse:wheel', this.onMouseWheel); document.removeEventListener('keydown', this.onKeyDown); document.removeEventListener('keyup', this.onKeyUp); @@ -217,12 +159,16 @@ export class CanvasControlsPlugin extends CanvasPlugin { return true; } - onMaskActivated() { + onBrushActivated() { + this.isBrushActivated = true; this.isPanMode = false; this.updatePanButtonState(); } - onMaskDeactivated() { + onBrushDeactivated() { + this.isBrushActivated = false; + this.isPanMode = true; + this.updatePanButtonState(); } onAfterRender() { @@ -235,57 +181,60 @@ export class CanvasControlsPlugin extends CanvasPlugin { } } - zoomIn() { - let zoom = this.canvas.getZoom(); - zoom *= 1.1; - if (zoom > 20) zoom = 20; - this.canvas.zoomToPoint({ x: this.canvas.width / 2, y: this.canvas.height / 2 }, zoom); - } - - zoomOut() { - let zoom = this.canvas.getZoom(); - zoom /= 1.1; - if (zoom < 0.1) zoom = 0.1; - this.canvas.zoomToPoint({ x: this.canvas.width / 2, y: this.canvas.height / 2 }, zoom); - } - - resetZoom() { - this.canvas.setViewportTransform([1, 0, 0, 1, 0, 0]); - } - - togglePanMode() { - this.isPanMode = !this.isPanMode; - this.updatePanButtonState(); - - if (this.isPanMode) { - this.canvas.setCursor('grab'); - this.canvasManager.emit('pan:activated'); - } else { - this.canvas.setCursor('default'); - this.canvasManager.emit('pan:deactivated'); - } - } - onKeyDown(e) { - if (e.altKey && !this.isAltPan) { - this.isAltPan = true; - this.updatePanButtonState(); - this.canvas.setCursor('grab'); - this.canvasManager.emit('pan:activated'); + const key = e.key; + switch (key) { + case 'Alt': + if (!this.isAltKeyPressed) { + this.isAltKeyPressed = true; + } + break; + case 'Control': + if (!this.isCtrlKeyPressed) { + this.isCtrlKeyPressed = true; + } + break; + case 'Shift': + if (!this.isShiftKeyPressed) { + this.isShiftKeyPressed = true; + } + break; + case 'h': + this.togglePanMode(); + break; + default: + break; } } onKeyUp(e) { - if (!e.altKey && this.isAltPan) { - this.isAltPan = false; - this.updatePanButtonState(); - this.canvas.setCursor(this.isPanMode ? 'grab' : 'default'); - this.canvasManager.emit('pan:deactivated'); + const key = e.key; + this.isDrawingMode = false; + + switch (key) { + case 'Alt': + if (this.isAltKeyPressed) { + this.isAltKeyPressed = false; + } + break; + case 'Control': + if (this.isCtrlKeyPressed) { + this.isCtrlKeyPressed = false; + } + break; + case 'Shift': + if (this.isShiftKeyPressed) { + this.isShiftKeyPressed = false; + } + break; + default: + break; } + e.preventDefault(); } onMouseDown(opt) { - if (this.isPanMode || this.isAltPan) { + if (this.isPanMode || this.isAltPan) { this.isPanning = true; const evt = opt.e; this.lastPosX = evt.clientX; @@ -303,8 +252,23 @@ export class CanvasControlsPlugin extends CanvasPlugin { const deltaY = e.clientY - this.lastPosY; const vpt = this.canvas.viewportTransform; const zoom = this.canvas.getZoom(); - vpt[4] += deltaX / zoom; - vpt[5] += deltaY / zoom; + // const panFactor = (zoom + 1) / 10; + let panFactor; + if (zoom <= 1) { + panFactor = (zoom + 1) / 2; + } else if (zoom <= 3) { + panFactor = (zoom + 2) / 4; + } else if (zoom <= 6) { + panFactor = (zoom + 3) / 7; + } else { + panFactor = (zoom + 1) / 10; + } + + // console.log('panFactor', panFactor); + // console.log('zoom', zoom); + + vpt[4] += deltaX * panFactor; + vpt[5] += deltaY * panFactor; this.canvas.requestRenderAll(); this.lastPosX = e.clientX; this.lastPosY = e.clientY; @@ -317,12 +281,224 @@ export class CanvasControlsPlugin extends CanvasPlugin { this.canvas.setCursor(this.isPanMode || this.isAltPan ? 'grab' : 'default'); } } + togglePanMode() { + this.isPanMode = !this.isPanMode; + this.updatePanButtonState(); + this.canvasManager.emit('pan:toggled'); + + // if (this.isPanMode) { + // this.canvas.setCursor('grab'); + // this.canvasManager.emit('pan:activated'); + + // } else { + // this.canvas.setCursor('default'); + // this.canvasManager.emit('pan:deactivated'); + // } + } + + zoomIn() { + let zoom = this.canvas.getZoom(); + zoom *= 1.1; + if (zoom > 20) zoom = 20; + this.canvas.zoomToPoint({ x: this.canvas.width / 2, y: this.canvas.height / 2 }, zoom); + } + + zoomOut() { + let zoom = this.canvas.getZoom(); + zoom /= 1.1; + if (zoom < 0.1) zoom = 0.1; + this.canvas.zoomToPoint({ x: this.canvas.width / 2, y: this.canvas.height / 2 }, zoom); + } + + resetZoom() { + this.canvas.setViewportTransform([1, 0, 0, 1, 0, 0]); + } + + + zoom(e,delta) { + let zoom = this.canvas.getZoom(); + zoom *= 0.999 ** delta; + if (zoom > 20) zoom = 20; + if (zoom < 0.1) zoom = 0.1; + this.canvas.zoomToPoint({ x: e.offsetX, y: e.offsetY }, zoom); + // this.canvasManager.emit('zoom:changed', { zoom }); + } + + horizontalPan(delta) { + const vpt = this.canvas.viewportTransform; + const panDelta = delta / 10; // Adjust pan speed as necessary + vpt[4] += panDelta; + this.canvas.requestRenderAll(); + // this.canvasManager.emit('pan:changed', { x: vpt[4], y: vpt[5] }); + } + + verticalPan(delta) { + const vpt = this.canvas.viewportTransform; + const panDelta = delta / 10; // Adjust pan speed as necessary + vpt[5] += panDelta; + this.canvas.requestRenderAll(); + // this.canvasManager.emit('pan:changed', { x: vpt[4], y: vpt[5] }); + } + + onMouseWheel(opt) { + const e = opt.e; + const delta = e.deltaY; + const pointer = this.canvas.getPointer(e); + // console.log('pointer', pointer); + // console.log('isDrawingMode', this.isDrawingMode); + if (!this.isDrawingMode) { + if (e.altKey) { + this.zoom(e,delta); + } else if (e.ctrlKey) { + this.horizontalPan(delta); + } else if (e.shiftKey) { + this.verticalPan(delta); + } else { + if (!this.isBrushActivated) { + this.zoom(e,delta); + } + } + } + e.preventDefault(); + e.stopPropagation(); + } destroy() { if (this.uiContainer && this.uiContainer.parentNode) { this.uiContainer.parentNode.removeChild(this.uiContainer); } - this.detachEventListeners(); } + + getHTML() { + return ` +
+ + + + +
+ + + + + ${this.switchViewToggleButton ? ` + + ` : ''} +
+ `; + } + getCSS() { + return` + .cc-container * { + box-sizing: border-box; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; + } + + .cc-container { + display: inline-flex; + gap: 0.5rem; + padding: 0.5rem; + user-select: none; + } + + .cc-button { + display: inline-flex; + align-items: center; + justify-content: center; + padding: 0.5rem; + background: var(--color-button-primary); + border: none; + cursor: pointer; + transition: all 0.2s; + color: var(--color-primary-text); + min-width: 36px; + height: 36px; + } + + .cc-button:hover { + background: var(--color-button-primary-hover); + } + + .cc-button svg { + width: 1.25rem; + height: 1.25rem; + } + + .cc-button[data-active="true"] { + border: 1px dashed var(--color-button-secondary-text-active); + } + + .cc-divider { + width: 1px; + border-left: 1px dashed var(--color-border); + margin: 0 0.25rem; + display: none; + } + #panBtn{ + + } + `; + } + getPanHandIcon() { + return ` + + + + + + + `; + } + getCanvasViewIcon() { + return ` + + + + `; + } + + getSplitViewIcon() { + return ` + + + + `; + } + } + + + + diff --git a/web/core/js/common/components/canvas/CanvasLoader.js b/web/core/js/common/components/canvas/CanvasLoader.js index 160113b..2b91f2a 100644 --- a/web/core/js/common/components/canvas/CanvasLoader.js +++ b/web/core/js/common/components/canvas/CanvasLoader.js @@ -39,7 +39,7 @@ const loadDependencies = async () => { } }; -function setView(view) { +function setView(viewType) { const canvasWrapper = document.getElementById("canvasWrapper"); const imageContainer = document.getElementById("image-container"); @@ -48,12 +48,12 @@ function setView(view) { return; } - switch(view) { - case 'output': + switch(viewType) { + case 'standardView': imageContainer.style.display = "flex"; canvasWrapper.style.display = "none"; break; - case 'canvas': + case 'canvasView': canvasWrapper.style.display = "flex"; imageContainer.style.display = "none"; break; @@ -62,13 +62,12 @@ function setView(view) { imageContainer.style.display = "flex"; break; default: - console.warn(`Unknown view state: ${view}. Defaulting to 'splitView'.`); - canvasWrapper.style.display = "flex"; - imageContainer.style.display = "flex"; + console.warn(`Unknown viewType state: ${viewType}. Defaulting to 'standardView'.`); + imageContainer.style.display = "flex"; + canvasWrapper.style.display = "none"; } } - export class CanvasLoader { constructor(canvasId, flowConfig) { this.canvasId = canvasId; @@ -77,10 +76,10 @@ export class CanvasLoader { this.initPromise = this.init(); store.subscribe((state) => { - setView(state.view); + setView(state.viewType); }); - setView(this.flowConfig.initialView || store.getState().view); + setView(this.flowConfig.initialView || store.getState().viewType); } determineCanvasOptions(flowConfig) { @@ -90,77 +89,84 @@ export class CanvasLoader { undo: true, maskBrush: false, customBrush: false, - imageCompareSlider: false, imageAdder: false, canvasScaleForSave: false, + imageCompareSlider: false, + switchViewToggleButton: false, }; - // **Case 1**: Load maskBrush if canvasLoadedImages and canvasSelectedMaskOutputs are present - if (flowConfig.canvasLoadedImages && Array.isArray(flowConfig.canvasLoadedImages) && flowConfig.canvasLoadedImages.length > 0 && - flowConfig.canvasAlphaOutputs && Array.isArray(flowConfig.canvasAlphaOutputs) && flowConfig.canvasAlphaOutputs.length > 0) { - options.maskBrush = true; + //img2img + // **Case 1**: Load customBrush if canvasOutputs are present + if (flowConfig.canvasOutputs && Array.isArray(flowConfig.canvasOutputs) && flowConfig.canvasOutputs.length > 0) { + options.customBrush = true; options.imageLoader = true; + options.canvasScaleForSave = true; + options.switchViewToggleButton = true; // Enable toggle button store.dispatch({ type: 'SET_VIEW', - payload: 'canvas' + payload: 'splitView' }); store.dispatch({ - type: 'SET_INPAINT_STYLE', - payload: 'full' + type: 'SET_MASKING_TYPE', + payload: 'none' }); } - - // **Case 2**: Load customBrush if canvasOutputs are present - if (flowConfig.canvasOutputs && Array.isArray(flowConfig.canvasOutputs) && flowConfig.canvasOutputs.length > 0) { - options.customBrush = true; + + //full alpha mask inpaint + // **Case 2**: Load maskBrush if canvasLoadedImages and canvasAlphaOutputs are present + if (flowConfig.canvasLoadedImages && Array.isArray(flowConfig.canvasLoadedImages) && flowConfig.canvasLoadedImages.length > 0 && + flowConfig.canvasAlphaOutputs && Array.isArray(flowConfig.canvasAlphaOutputs) && flowConfig.canvasAlphaOutputs.length > 0) { + options.maskBrush = true; options.imageLoader = true; - options.canvasScaleForSave = true; + options.switchViewToggleButton = true; // Enable toggle button + store.dispatch({ type: 'SET_VIEW', - payload: 'splitView' + payload: 'canvasView' + }); + store.dispatch({ + type: 'SET_MASKING_TYPE', + payload: 'full' }); } - // // **Case 3**: Load canvasCroppedImageOutputs if canvasCroppedMaskOutputs are present - // if (flowConfig.canvasCroppedMaskOutputs && Array.isArray(flowConfig.canvasCroppedMaskOutputs) && flowConfig.canvasCroppedMaskOutputs.length > 0) { - // options.maskBrush = true; - // options.imageLoader = true; - // store.dispatch({ - // type: 'SET_VIEW', - // payload: 'canvas' - // }); - // } - - //MASK + + //cropped bw mask inpaint // **Case 4**: Load canvasCroppedImageOutputs and canvasCroppedMaskOutputs and canvasLoadedImages are present if (flowConfig.canvasCroppedImageOutputs && Array.isArray(flowConfig.canvasCroppedImageOutputs) && flowConfig.canvasCroppedImageOutputs.length > 0 && flowConfig.canvasCroppedMaskOutputs && Array.isArray(flowConfig.canvasCroppedMaskOutputs) && flowConfig.canvasCroppedMaskOutputs.length > 0 && flowConfig.canvasLoadedImages && Array.isArray(flowConfig.canvasLoadedImages) && flowConfig.canvasLoadedImages.length > 0) { options.maskBrush = true; options.imageLoader = true; + options.switchViewToggleButton = true; // Enable toggle button + store.dispatch({ type: 'SET_VIEW', - payload: 'canvas' + payload: 'canvasView' }); store.dispatch({ - type: 'SET_INPAINT_STYLE', + type: 'SET_MASKING_TYPE', payload: 'cropped' }); } - //ALPHA MASK + //cropped alpha mask inpaint + // **Case 5**: Load canvasCroppedImageOutputs and canvasCroppedAlphaOnImageOutputs and canvasLoadedImages are present if (flowConfig.canvasCroppedImageOutputs && Array.isArray(flowConfig.canvasCroppedImageOutputs) && flowConfig.canvasCroppedImageOutputs.length > 0 && flowConfig.canvasCroppedAlphaOnImageOutputs && Array.isArray(flowConfig.canvasCroppedAlphaOnImageOutputs) && flowConfig.canvasCroppedAlphaOnImageOutputs.length > 0 && flowConfig.canvasLoadedImages && Array.isArray(flowConfig.canvasLoadedImages) && flowConfig.canvasLoadedImages.length > 0) { options.maskBrush = true; options.imageLoader = true; + options.imageCompareSlider= true; + options.switchViewToggleButton = true; // Enable toggle button + store.dispatch({ type: 'SET_VIEW', - payload: 'canvas' + payload: 'canvasView' }); store.dispatch({ - type: 'SET_INPAINT_STYLE', - payload: 'cropped' + type: 'SET_MASKING_TYPE', + payload: 'full' }); } return options; @@ -170,15 +176,12 @@ export class CanvasLoader { try { this.options = this.determineCanvasOptions(this.flowConfig); - if (!this.options.maskBrush && !this.options.customBrush) { - console.log("No relevant fields in flowConfig. Canvas will not be displayed or initialized."); + console.warn("No relevant fields in flowConfig. Canvas will not be displayed or initialized."); this.hideCanvasUI(); return; } - // await loadFabric(); - // loadScript('/core/js/common/components/canvas/fabric.5.2.4.min.js') await loadDependencies(); const canvasWrapper = document.getElementById('canvasWrapper'); const pluginUIContainer = document.getElementById('pluginUIContainer'); @@ -200,7 +203,9 @@ export class CanvasLoader { } if (this.options.canvasControls) { - const canvasControlsPlugin = new CanvasControlsPlugin(); + const canvasControlsPlugin = new CanvasControlsPlugin({ + switchViewToggleButton: this.options.switchViewToggleButton + }); this.canvasManager.registerPlugin(canvasControlsPlugin); } @@ -268,7 +273,32 @@ export class CanvasLoader { this.canvasManager.registerPlugin(imageAdderPlugin); } - console.log('All selected plugins registered successfully'); + console.log('All plugins registered successfully'); + + this.canvasManager.on('switchView', (newView) => { + // console.log(`Switching viewType to: ${newView}`); + setView(newView); + + store.dispatch({ + type: 'SET_VIEW', + payload: newView + }); + + + + }); + this.canvasManager.on('switchMaskingPreviewType', (PreviewType) => { + console.log(`Switching switchMaskingPreviewType to: ${PreviewType}`); + // setView(PreviewType); + + store.dispatch({ + type: 'SET_MASKING_TYPE', + payload: PreviewType + }); + + + + }); this.isInitialized = true; } catch (error) { @@ -397,4 +427,5 @@ export class CanvasLoader { } } + } diff --git a/web/core/js/common/components/canvas/CustomBrushPlugin.js b/web/core/js/common/components/canvas/CustomBrushPlugin.js index 805628e..4f93ebe 100644 --- a/web/core/js/common/components/canvas/CustomBrushPlugin.js +++ b/web/core/js/common/components/canvas/CustomBrushPlugin.js @@ -24,8 +24,11 @@ export class CustomBrushPlugin extends CanvasPlugin { this.canvasManager = null; this.canvas = null; this.drawingMode = false; + this.brushToggledByKey = false; this.isMouseDown = false; this.lastPointer = null; + this.isMouseOverCanvas = false; + this.currentPath = null; this.brushIcon = '/core/media/ui/paintree.png'; @@ -76,6 +79,9 @@ export class CustomBrushPlugin extends CanvasPlugin { this.onModeChange = this.onModeChange.bind(this); this.brushStrokeIdCounter = 0; this.disableDrawingMode = this.disableDrawingMode.bind(this); + this.enableDrawingMode = this.enableDrawingMode.bind(this); + this.onCanvasPointerEnter = this.onCanvasPointerEnter.bind(this); + this.onCanvasPointerLeave = this.onCanvasPointerLeave.bind(this); } init(canvasManager) { @@ -90,6 +96,13 @@ export class CustomBrushPlugin extends CanvasPlugin { this.createUI(); + // if (this.canvasElement) { + // if (this.canvasElement.tagName.toLowerCase() === 'canvas' && !this.canvasElement.hasAttribute('tabindex')) { + // this.canvasElement.setAttribute('tabindex', '0'); + // // console.log('Set tabindex="0" on the canvas element to make it focusable.'); + // } + // } + this.attachEventListeners(); this.brushSizeInput.value = this.brushSize; @@ -101,6 +114,7 @@ export class CustomBrushPlugin extends CanvasPlugin { this.canvas.getObjects().forEach(this.enforceObjectProperties); this.canvasManager.on('viewport:changed', this.onViewportChanged); + this.canvasManager.on('pan:toggled', this.disableDrawingMode); } injectHiddenCursorCSS() { @@ -168,12 +182,10 @@ export class CustomBrushPlugin extends CanvasPlugin { -
@@ -352,7 +344,6 @@ export class MaskBrushPlugin extends CustomBrushPlugin { this.hideMaskCheckbox.addEventListener('change', this.onToggleHideMask); this.hideAllMasksCheckbox.addEventListener('change', this.onToggleHideAllMasks); this.saveMaskBtn.addEventListener('click', this.onHandleSave); - this.disableDrawingModeBtn.addEventListener('click', this.disableDrawingMode); } detachAdditionalEventListeners() { @@ -368,7 +359,6 @@ export class MaskBrushPlugin extends CustomBrushPlugin { this.hideMaskCheckbox.removeEventListener('change', this.onToggleHideMask); this.hideAllMasksCheckbox.removeEventListener('change', this.onToggleHideAllMasks); this.saveMaskBtn.removeEventListener('click', this.onHandleSave); - this.disableDrawingModeBtn.removeEventListener('click', this.disableDrawingMode); this.canvasManager.off('image:loaded', this.onImageLoaded); this.canvasManager.off('canvas:state:changed', this.onCanvasStateChanged); @@ -391,8 +381,8 @@ export class MaskBrushPlugin extends CustomBrushPlugin { } handleStateChange(state) { - console.log('---Current state---', state.hideMask); - console.log('---this.clearMasksOnImageLoaded ---', this.clearMasksOnImageLoaded ); + // console.log('---Current state---', state.hideMask); + // console.log('---this.clearMasksOnImageLoaded ---', this.clearMasksOnImageLoaded ); if (this.clearMasksOnImageLoaded == false) { @@ -436,6 +426,9 @@ export class MaskBrushPlugin extends CustomBrushPlugin { this.maskStrokeHistory = {}; this.onAddMask(); + // if (!this.drawingMode) { + // this.disableDrawingMode(); + // } } else { this.masks.forEach(mask => { this.canvas.remove(mask.fabricImage); @@ -601,35 +594,6 @@ export class MaskBrushPlugin extends CustomBrushPlugin { this.updateBrushColorAndCursor(); } - disableDrawingMode() { - super.disableDrawingMode(); - } - - onPanActivated() { - this.disableDrawingMode(); - - } - - onPanDeactivated() { - // this.disableDrawingMode(); - } - - - onToggleDrawingMode() { - super.onToggleDrawingMode(); - - if (this.drawingMode) { - this.canvasManager.emit('mask:activated'); - } - - // console.log('onToggleDrawingModemask:', store); - - // store.dispatch({ - // type: 'TOGGLE_MASK', - // payload: true - // }); - } - onChangeMaskColor() { const color = this.brushColor = this.colorPicker.value; const selectedOption = this.maskList.options[this.maskList.selectedIndex]; @@ -692,7 +656,6 @@ export class MaskBrushPlugin extends CustomBrushPlugin { } onApplyColorToExistingMaskChange() { - // Additional actions if needed when the checkbox state changes } onMoveMaskUp() { @@ -888,8 +851,8 @@ export class MaskBrushPlugin extends CustomBrushPlugin { onMouseMove(o) { if (!this.currentMask || !this.currentMask.ctx) { - console.error('Cannot draw: currentMask or its context is null.'); - return; + console.warn('Cannot draw: currentMask or its context is null.'); + // return; } const pointer = this.canvas.getPointer(o.e, true); diff --git a/web/core/js/common/components/canvas/MaskExportUtilities.js b/web/core/js/common/components/canvas/MaskExportUtilities.js index 6f9b1f9..5e005ea 100644 --- a/web/core/js/common/components/canvas/MaskExportUtilities.js +++ b/web/core/js/common/components/canvas/MaskExportUtilities.js @@ -160,7 +160,8 @@ export class MaskExportUtilities { const { minX, minY, maxX, maxY } = this._getBoundingBox(canvas); if (maxX < minX || maxY < minY) { - alert('Selected mask is completely transparent.'); + // alert('Selected mask is completely transparent.'); + return null; } @@ -179,7 +180,7 @@ export class MaskExportUtilities { const config = { ...this.config, ...overrides }; if (!this.plugin.currentMask) { - alert('No mask selected to export.'); + // alert('No mask selected to export.'); return null; } diff --git a/web/core/js/common/components/messageHandler.js b/web/core/js/common/components/messageHandler.js index e88c5b2..0353460 100644 --- a/web/core/js/common/components/messageHandler.js +++ b/web/core/js/common/components/messageHandler.js @@ -1,6 +1,5 @@ -// messageHandler.js import { WebSocketHandler } from './webSocketHandler.js'; -import { updateProgress, displayImagesInDiv } from './imagedisplay.js'; +import { updateProgress, displayImagesInDiv as displayOutputMedia } from './imagedisplay.js'; import { hideSpinner } from './utils.js'; import { store } from '../scripts/stateManagerMain.js'; class IMessageProcessor { @@ -80,6 +79,7 @@ async function detectMimeType(blob) { return null; } + class BlobMessageProcessor extends IMessageProcessor { constructor(messageHandler) { super(); @@ -88,59 +88,50 @@ class BlobMessageProcessor extends IMessageProcessor { async process(blob) { try { - const { view } = store.getState(); - const { croppedImage } = store.getState(); - const { inpiantStyle } = store.getState(); + let result = {}; + if (!blob.type) { const headerSize = 8; if (blob.size <= headerSize) { console.error('Blob size is too small to contain valid image data.'); hideSpinner(); - return; + result.error = 'Blob size is too small to contain valid image data.'; + return result; } - const slicedBlob = blob.slice(headerSize); const detectedType = await detectMimeType(slicedBlob); const objectURL = URL.createObjectURL(slicedBlob); if (detectedType) { - switch(view) { - case 'output': - this.messageHandler.handleMedia(objectURL, detectedType, false); - break; - case 'canvas': - switch(inpiantStyle) { - case 'full': - this.messageHandler.overlayPreviewOnOriginal(objectURL, detectedType, false); - // console.log('Blob message inpiantStyle full:', inpiantStyle); - break; - case 'cropped': - this.messageHandler.compositeCroppedPreviewOnOriginal(objectURL, croppedImage); - // console.log('Blob message inpiantStyle cropped:', inpiantStyle); - break; - } - break; - case 'splitView': - this.messageHandler.handleMedia(objectURL, detectedType, false); - // this.messageHandler.overlayPreviewOnOriginal(objectURL); - break; - } + result = { + objectURL, + detectedType, + isTypeDetected: true + }; } else { console.error('Could not detect MIME type of Blob.'); hideSpinner(); + result.error = 'Could not detect MIME type of Blob.'; } - return; + return result; } if (blob.type.startsWith('image/') || blob.type.startsWith('video/')) { const objectURL = URL.createObjectURL(blob); - this.messageHandler.handleMedia(objectURL, blob.type, true); + result = { + objectURL, + detectedType: blob.type, + isTypeDetected: true + }; } else { console.error('Unsupported Blob type:', blob.type); hideSpinner(); + result.error = 'Unsupported Blob type: ' + blob.type; } + return result; } catch (error) { console.error('Error processing Blob message:', error); hideSpinner(); + return { error }; } } } @@ -157,6 +148,7 @@ export class MessageHandler { this.maskImageDataURL = null; this.canvasCroppedMaskOutputs = null; this.AlphaMaskImageDataURL = null; + this.imageDataType = null; } setOriginalImage(dataURL) { @@ -175,7 +167,6 @@ export class MessageHandler { this.AlphaMaskImageDataURL = dataURL; // console.log('Alpha mask image set for MaskModePreview.'); } - setCanvasSelectedMaskOutputs(dataURL) { if (!dataURL || typeof dataURL !== 'string') { console.error('Canvas Selected Mask Outputs should be an array of Data URLs.'); @@ -184,7 +175,6 @@ export class MessageHandler { this.canvasSelectedMaskOutputs = dataURL; // console.log('Canvas Selected Mask Outputs set successfully.'); } - setCroppedMaskImage(dataURL) { if (!dataURL || typeof dataURL !== 'string') { console.error('Canvas Cropped Mask Outputs should be an array of Data URLs.'); @@ -193,7 +183,6 @@ export class MessageHandler { this.canvasCroppedMaskOutputs = dataURL; // console.log('Canvas Cropped Mask Outputs set successfully.'); } - setMaskImage(dataURL) { if (!dataURL || typeof dataURL !== 'string') { console.error('Mask image Data URL is invalid.'); @@ -203,31 +192,65 @@ export class MessageHandler { // console.log('Mask image set for MaskModePreview.'); } - handleMessage(event) { + handlePreviewOutput(result) { + if (result.error) { + console.error(result.error); + return; + } + const { viewType, croppedImage, maskingType } = store.getState(); + this.imageDataType = 'previewImageData'; + if (result.isTypeDetected) { + const { objectURL, detectedType} = result; + switch(viewType) { + case 'standardView': + this.setStandardPreview(objectURL); + break; + case 'splitView': + this.setStandardPreview(objectURL); + break; + case 'canvasView': + switch(maskingType) { + case 'none': + this.canvasPreview(objectURL) + break; + case 'full': + this.canvasFullMaskPreview(objectURL); + break; + case 'cropped': + this.canvasCroppedMaskPreview(objectURL, croppedImage); + break; + } + break; + default: + console.warn('Unknown viewType type:', viewType); + } + } else { + console.error('Type not detected or unsupported.'); + } + } + + handlePreviewOutputMessage(event) { if (typeof event.data === 'string') { this.jsonProcessor.process(event.data); - - // preview output } else if (event.data instanceof Blob) { - // console.log('Preview image:'); - this.blobProcessor.process(event.data); + this.blobProcessor.process(event.data).then(result => { + this.handlePreviewOutput(result); + }); } else { console.warn('Unknown message type:', typeof event.data); } - } + } handleProgress(data) { this.hideSpinnerOnce(); updateProgress(data.max, data.value); } - hideSpinnerOnce() { if (!this.spinnerHidden) { hideSpinner(); this.spinnerHidden = true; } } - handleMonitor(data) { console.log('Monitor data received:', data); } @@ -235,11 +258,11 @@ export class MessageHandler { handleExecuted(data) { if (data.output) { if ('images' in data.output) { - this.processImages(data.output.images); + this.processFinalImageOutput(data.output.images); } - + //gifs = videos if ('gifs' in data.output) { - this.processGifs(data.output.gifs); + this.processFinalVideoOutput(data.output.gifs); } } hideSpinner(); @@ -249,7 +272,7 @@ export class MessageHandler { window.dispatchEvent(event); } - async processImages(images) { + async processFinalImageOutput(images) { const newImageFilenames = []; const imageUrls = images.map(image => { const { filename } = image; @@ -257,13 +280,9 @@ export class MessageHandler { if (filename.includes('ComfyUI_temp')) { return null; } - // Detect original image based on filename - if (filename.toLowerCase().includes('original')) { - // Original image is handled separately - return null; - } + if (this.lastImageFilenames.includes(filename)) { - console.log('Duplicate image:', filename); + // console.log('Duplicate image:', filename); return null; } @@ -272,37 +291,37 @@ export class MessageHandler { console.log('processImages Image URL:', imageUrl); return imageUrl; }).filter(url => url !== null); - console.log('Image URLs:', imageUrls.length); + if (imageUrls.length > 0) { - displayImagesInDiv(imageUrls); - for (const url of imageUrls) { - try { - const { view } = store.getState(); + displayOutputMedia(imageUrls); + this.displayPreviewOutput(imageUrls) + this.lastImageFilenames = newImageFilenames; + return imageUrls; + } + } - if (view === 'canvas') { - this.emitCombinedImage(url) - } - } catch (error) { - console.error('Error overlaying preview on image:', url, error); + displayPreviewOutput(imageUrls) { + for (const url of imageUrls) { + try { + const { viewType } = store.getState(); + if (viewType === 'canvasView') { + this.imageDataType = 'finalImageData'; + this.emitCombinedImage(url) } + } catch (error) { + console.error('Error overlaying preview on image:', url, error); } - this.lastImageFilenames = newImageFilenames; } } - processGifs(gifs) { - const gifUrls = gifs.map(gif => { - const { filename } = gif; - const gifUrl = `/view?filename=${encodeURIComponent(filename)}`; - return gifUrl; + processFinalVideoOutput(videos) { + const videosUrls = videos.map(video => { + const { filename } = video; + const videoUrl = `/view?filename=${encodeURIComponent(filename)}`; + return videoUrl; }); - console.log('GIF URLs:', gifUrls); - displayImagesInDiv(gifUrls); - } - - handleMedia(mediaUrl, mediaType, addToHistory = true) { - displayImagesInDiv([mediaUrl], addToHistory); + displayOutputMedia(videosUrls); } handleInterrupted() { @@ -317,7 +336,16 @@ export class MessageHandler { updateProgress(); } - async compositeCroppedPreviewOnOriginal(previewUrl, croppedImage) { + setStandardPreview(mediaUrl, addToHistory = false) { + displayOutputMedia([mediaUrl], addToHistory); + } + + + async canvasPreview(objectURL) { + this.emitCombinedImage(objectURL); + + } + async canvasCroppedMaskPreview(previewUrl, croppedImage) { if (!this.originalImageDataURL || !this.canvasCroppedMaskOutputs) { console.error('Original image data URL or mask is not provided. Cannot overlay preview.'); return null; @@ -331,20 +359,14 @@ export class MessageHandler { ]); const { x, y, width, height } = croppedImage.mask; - const mainCanvas = this.createCanvas(originalImage.width, originalImage.height); const mainCtx = mainCanvas.getContext('2d'); - mainCtx.drawImage(originalImage, 0, 0); - const maskedPreview = this.prepareMaskedPreview(previewImage, maskImage, width, height); - mainCtx.drawImage(maskedPreview, x, y, width, height); + const JPEG_QUALITY = 0.7; - const JPEG_QUALITY = 0.5; - - const combinedDataURL = mainCanvas.toDataURL('image/jpeg', JPEG_QUALITY); - + const combinedDataURL = mainCanvas.toDataURL('image/webp', JPEG_QUALITY); if (combinedDataURL) { this.emitCombinedImage(combinedDataURL); } @@ -356,59 +378,6 @@ export class MessageHandler { } } - async loadImages(urls) { - return Promise.all(urls.map(url => this.loadImage(url))); - } - - - createCanvas(width, height) { - const canvas = document.createElement('canvas'); - canvas.width = width; - canvas.height = height; - return canvas; - } - - prepareMaskedPreview(previewImage, maskImage, width, height) { - const maskCanvas = this.createCanvas(width, height); - const previewCanvas = this.createCanvas(width, height); - - const maskCtx = maskCanvas.getContext('2d'); - const previewCtx = previewCanvas.getContext('2d'); - - const BLUR_RADIUS = 3; - maskCtx.filter = `blur(${BLUR_RADIUS}px)`; - maskCtx.drawImage(maskImage, 0, 0, width, height); - maskCtx.filter = 'none'; - - previewCtx.drawImage(previewImage, 0, 0, width, height); - - const blurredMaskData = maskCtx.getImageData(0, 0, width, height); - const previewData = previewCtx.getImageData(0, 0, width, height); - - const maskedData = this.applyMaskToAlpha(previewData, blurredMaskData); - - previewCtx.putImageData(maskedData, 0, 0); - - return previewCanvas; - } - - applyMaskToAlpha(previewData, maskData) { - const data = previewData.data; - const mask = maskData.data; - - if (data.length !== mask.length) { - throw new Error('Preview and mask ImageData must have the same dimensions.'); - } - - for (let i = 0; i < data.length; i += 4) { - const maskAlpha = mask[i]; - const normalizedAlpha = maskAlpha / 255; - data[i + 3] = normalizedAlpha * data[i + 3]; - } - - return previewData; - } - async compositePreviewOnOriginal(previewUrl, invertMask = true) { if (!this.originalImageDataURL || !this.AlphaMaskImageDataURL) { console.error('Original image or mask image is not set. Cannot overlay preview.'); @@ -447,6 +416,7 @@ export class MessageHandler { } maskCtx.putImageData(maskImageData, 0, 0); } + let minX = maskCanvas.width, minY = maskCanvas.height, maxX = 0, maxY = 0; for (let y = 0; y < maskCanvas.height; y++) { @@ -524,9 +494,9 @@ export class MessageHandler { ctx.drawImage(tempCanvas, 0, 0); - const JPEG_QUALITY = 0.5; + const JPEG_QUALITY = 0.7; - const combinedDataURL = canvas.toDataURL('image/jpeg', JPEG_QUALITY); + const combinedDataURL = canvas.toDataURL('image/webp', JPEG_QUALITY); return combinedDataURL; @@ -536,19 +506,71 @@ export class MessageHandler { } } + async loadImages(urls) { + return Promise.all(urls.map(url => this.loadImage(url))); + } + + createCanvas(width, height) { + const canvas = document.createElement('canvas'); + canvas.width = width; + canvas.height = height; + return canvas; + } + + prepareMaskedPreview(previewImage, maskImage, width, height) { + const maskCanvas = this.createCanvas(width, height); + const previewCanvas = this.createCanvas(width, height); + + const maskCtx = maskCanvas.getContext('2d'); + const previewCtx = previewCanvas.getContext('2d'); + + const BLUR_RADIUS = 3; + maskCtx.filter = `blur(${BLUR_RADIUS}px)`; + maskCtx.drawImage(maskImage, 0, 0, width, height); + maskCtx.filter = 'none'; + + previewCtx.drawImage(previewImage, 0, 0, width, height); + + const blurredMaskData = maskCtx.getImageData(0, 0, width, height); + const previewData = previewCtx.getImageData(0, 0, width, height); + + const maskedData = this.applyMaskToAlpha(previewData, blurredMaskData); + + previewCtx.putImageData(maskedData, 0, 0); + + return previewCanvas; + } + + applyMaskToAlpha(previewData, maskData) { + const data = previewData.data; + const mask = maskData.data; + + if (data.length !== mask.length) { + throw new Error('Preview and mask ImageData must have the same dimensions.'); + } + + for (let i = 0; i < data.length; i += 4) { + const maskAlpha = mask[i]; + const normalizedAlpha = maskAlpha / 255; + data[i + 3] = normalizedAlpha * data[i + 3]; + } + + return previewData; + } + emitCombinedImage(combinedDataURL) { if (!combinedDataURL) { console.error('No combined image data to emit.'); return; } - - const previewImageEvent = new CustomEvent('previewImageLoaded', { + // console.log('Emitting combined image imageDataType.',); + const previewImageEvent = new CustomEvent(this.imageDataType, { detail: combinedDataURL }); window.dispatchEvent(previewImageEvent); } - async overlayPreviewOnOriginal(previewUrl, invertMask = true) { + async canvasFullMaskPreview(previewUrl, invertMask = true) { const combinedDataURL = await this.compositePreviewOnOriginal(previewUrl, invertMask); if (combinedDataURL) { @@ -586,7 +608,7 @@ export function initializeWebSocket(clientId) { const serverAddress = `${window.location.hostname}:${window.location.port}`; const wsHandler = new WebSocketHandler( `${protocol}://${serverAddress}/ws?clientId=${encodeURIComponent(clientId)}`, - (event) => messageHandler.handleMessage(event) + (event) => messageHandler.handlePreviewOutputMessage(event) ); wsHandler.connect(); return wsHandler; diff --git a/web/core/js/common/scripts/stateManagerMain.js b/web/core/js/common/scripts/stateManagerMain.js index b44e25a..41afbca 100644 --- a/web/core/js/common/scripts/stateManagerMain.js +++ b/web/core/js/common/scripts/stateManagerMain.js @@ -23,7 +23,7 @@ class StateManager { } subscribe(listener) { - console.log('New subscriber added to StateManager'); + // console.log('New subscriber added to StateManager'); if (typeof listener !== 'function') { throw new TypeError('Listener must be a function'); } @@ -32,13 +32,13 @@ class StateManager { listener(this.getState()); return () => { - console.log('Subscriber removed from StateManager'); + // console.log('Subscriber removed from StateManager'); this.listeners.delete(listener); }; } dispatch(action) { - console.log('Action dispatched:', action); + // console.log('Action dispatched:', action); if (!action || typeof action !== 'object' || !action.type) { throw new TypeError('Action must be an object with a type property'); } @@ -69,15 +69,15 @@ class StateManager { reducer(state, action) { switch (action.type) { case 'SET_VIEW': - return { ...state, view: action.payload }; + return { ...state, viewType: action.payload }; case 'TOGGLE_MASK': return { ...state, hideMask: !state.hideMask }; case 'SET_HIDE_MASK': return { ...state, hideMask: action.payload }; case 'SET_CROPPED_IMAGE': return { ...state, croppedImage: action.payload }; - case 'SET_INPAINT_STYLE': - return { ...state, inpiantStyle: action.payload }; + case 'SET_MASKING_TYPE': + return { ...state, maskingType: action.payload }; case 'RESET_STATE': return this.constructor.initialState; default: @@ -86,7 +86,7 @@ class StateManager { } notifyListeners() { - console.log('Notifying', this.listeners.size, 'listeners'); + // console.log('Notifying', this.listeners.size, 'listeners'); this.listeners.forEach(listener => { try { listener(this.getState()); @@ -107,13 +107,13 @@ class StateManager { if (this.history.length > this.historyLimit) { this.history.shift(); } - console.log('History updated, current length:', this.history.length); + // console.log('History updated, current length:', this.history.length); } } export const store = new StateManager({ - view: 'output', // Possible values: 'output', 'canvas', 'splitView' + viewType: 'standardView', // Possible values: 'standardView', 'canvasView', 'splitView' hideMask:false, croppedImage: {}, - inpiantStyle: 'full', // Possible values: 'full', 'cropped' + maskingType: 'full', // Possible values: 'full', 'cropped' });