refactor: centralize fit-on-add policy
This commit is contained in:
+5
-10
@@ -4,6 +4,7 @@ import { showErrorNotification } from "./utils/NotificationUtils.js";
|
||||
import { webSocketManager } from "./utils/WebSocketManager.js";
|
||||
import { scaleImageToFit, loadImage, blobToDataUrl, tensorToImageData, createImageFromImageData } from "./utils/ImageUtils.js";
|
||||
import { postImageBlob } from "./utils/ImageUploadUtils.js";
|
||||
import { getImageAddMode, isFitOnAddEnabled } from "./utils/CanvasInputUtils.js";
|
||||
const log = createModuleLogger('CanvasIO');
|
||||
function imageBatchIdentity(sources) {
|
||||
return sources.join('|');
|
||||
@@ -432,8 +433,7 @@ export class CanvasIO {
|
||||
log.info("Image change detected, will add new layers");
|
||||
}
|
||||
// Determine add mode
|
||||
const fitOnAddWidget = this.canvas.node.widgets.find((w) => w.name === "fit_on_add");
|
||||
const addMode = (fitOnAddWidget && fitOnAddWidget.value) ? 'fit' : 'center';
|
||||
const addMode = getImageAddMode(this.canvas.node.widgets);
|
||||
// Add all images from the batch as separate layers
|
||||
await this.addBatchImages(sourceNode.imgs, addMode, this.canvas.outputAreaBounds, 'to canvas');
|
||||
this.canvas.inputDataLoaded = true;
|
||||
@@ -522,9 +522,7 @@ export class CanvasIO {
|
||||
// Convert to HTMLImageElement
|
||||
const maskImg = await loadImage(maskCanvas.toDataURL());
|
||||
// Respect fit_on_add (scale to output area)
|
||||
const widgets = this.canvas.node.widgets;
|
||||
const fitOnAddWidget = widgets ? widgets.find((w) => w.name === "fit_on_add") : null;
|
||||
const shouldFit = fitOnAddWidget && fitOnAddWidget.value;
|
||||
const shouldFit = isFitOnAddEnabled(this.canvas.node.widgets);
|
||||
let finalMaskImg = maskImg;
|
||||
if (shouldFit) {
|
||||
const bounds = this.canvas.outputAreaBounds;
|
||||
@@ -634,9 +632,7 @@ export class CanvasIO {
|
||||
// Mark that we've loaded input data for this execution
|
||||
this.canvas.inputDataLoaded = true;
|
||||
// Determine add mode based on fit_on_add setting
|
||||
const widgets = this.canvas.node.widgets;
|
||||
const fitOnAddWidget = widgets ? widgets.find((w) => w.name === "fit_on_add") : null;
|
||||
const addMode = (fitOnAddWidget && fitOnAddWidget.value) ? 'fit' : 'center';
|
||||
const addMode = getImageAddMode(this.canvas.node.widgets);
|
||||
// Load input image(s) only if image input is actually connected, not already loaded, and allowed
|
||||
if (allowImage && !imageLoaded && hasImageInput) {
|
||||
if (inputData.input_images_batch) {
|
||||
@@ -670,8 +666,7 @@ export class CanvasIO {
|
||||
// Load mask image
|
||||
const maskImg = await loadImage(inputData.input_mask);
|
||||
// Determine if we should fit the mask or use it at original size
|
||||
const fitOnAddWidget2 = this.canvas.node.widgets.find((w) => w.name === "fit_on_add");
|
||||
const shouldFit = fitOnAddWidget2 && fitOnAddWidget2.value;
|
||||
const shouldFit = isFitOnAddEnabled(this.canvas.node.widgets);
|
||||
let finalMaskImg = maskImg;
|
||||
if (shouldFit && this.canvas.maskTool) {
|
||||
const bounds = this.canvas.outputAreaBounds;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { createModuleLogger } from "./log_system/log_funcs.js";
|
||||
import { snapToGrid, getSnapAdjustment, isPointInRotatedLayer } from "./utils/CommonUtils.js";
|
||||
import { loadImageFromBlob } from "./utils/ImageUtils.js";
|
||||
import { getImageAddMode } from "./utils/CanvasInputUtils.js";
|
||||
const log = createModuleLogger('CanvasInteractions');
|
||||
export class CanvasInteractions {
|
||||
constructor(canvas) {
|
||||
@@ -1108,8 +1109,7 @@ export class CanvasInteractions {
|
||||
}
|
||||
async loadDroppedImageFile(file, worldCoords) {
|
||||
void loadImageFromBlob(file).then(async (img) => {
|
||||
const fitOnAddWidget = this.canvas.node.widgets.find((w) => w.name === "fit_on_add");
|
||||
const addMode = fitOnAddWidget && fitOnAddWidget.value ? 'fit' : 'center';
|
||||
const addMode = getImageAddMode(this.canvas.node.widgets);
|
||||
await this.canvas.canvasLayers.addLayerWithImage(img, {}, addMode);
|
||||
}).catch(error => {
|
||||
log.error(`Failed to load dropped image: ${file.name}`, error);
|
||||
|
||||
+3
-4
@@ -15,6 +15,7 @@ import { iconLoader, LAYERFORGE_TOOLS } from "./utils/IconLoader.js";
|
||||
import { exportCanvasImage } from "./utils/CanvasExportUtils.js";
|
||||
import { getFlattenedCanvasBlob } from "./utils/CanvasBlobUtils.js";
|
||||
import { loadPreviewImage } from "./utils/PreviewUtils.js";
|
||||
import { getImageAddMode } from "./utils/CanvasInputUtils.js";
|
||||
import { fetchMattingModelStatus } from "./utils/MattingUtils.js";
|
||||
import { setupSAMDetectorHook } from "./SAMDetectorIntegration.js";
|
||||
const log = createModuleLogger('Canvas_view');
|
||||
@@ -418,8 +419,7 @@ async function createCanvasWidget(node, widget, app) {
|
||||
textContent: "Add Image",
|
||||
title: "Add image from file",
|
||||
onclick: () => {
|
||||
const fitOnAddWidget = node.widgets.find((w) => w.name === "fit_on_add");
|
||||
const addMode = fitOnAddWidget && fitOnAddWidget.value ? 'fit' : 'center';
|
||||
const addMode = getImageAddMode(node.widgets);
|
||||
const input = document.createElement('input');
|
||||
input.type = 'file';
|
||||
input.accept = 'image/*';
|
||||
@@ -447,8 +447,7 @@ async function createCanvasWidget(node, widget, app) {
|
||||
textContent: "Paste Image",
|
||||
title: "Paste image from clipboard",
|
||||
onclick: () => {
|
||||
const fitOnAddWidget = node.widgets.find((w) => w.name === "fit_on_add");
|
||||
const addMode = fitOnAddWidget && fitOnAddWidget.value ? 'fit' : 'center';
|
||||
const addMode = getImageAddMode(node.widgets);
|
||||
canvas.canvasLayers.handlePaste(addMode);
|
||||
}
|
||||
}),
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
export function isFitOnAddEnabled(widgets) {
|
||||
const fitOnAddWidget = widgets?.find(widget => widget.name === 'fit_on_add');
|
||||
return Boolean(fitOnAddWidget?.value);
|
||||
}
|
||||
export function getImageAddMode(widgets) {
|
||||
return isFitOnAddEnabled(widgets) ? 'fit' : 'center';
|
||||
}
|
||||
+5
-10
@@ -4,6 +4,7 @@ import { showErrorNotification } from "./utils/NotificationUtils.js";
|
||||
import { webSocketManager } from "./utils/WebSocketManager.js";
|
||||
import { scaleImageToFit, loadImage, blobToDataUrl, tensorToImageData, createImageFromImageData } from "./utils/ImageUtils.js";
|
||||
import { postImageBlob } from "./utils/ImageUploadUtils.js";
|
||||
import { getImageAddMode, isFitOnAddEnabled } from "./utils/CanvasInputUtils.js";
|
||||
import type { Canvas } from './Canvas';
|
||||
import type { Layer, Shape, AddMode } from './types';
|
||||
|
||||
@@ -537,8 +538,7 @@ export class CanvasIO {
|
||||
}
|
||||
|
||||
// Determine add mode
|
||||
const fitOnAddWidget = this.canvas.node.widgets.find((w) => w.name === "fit_on_add");
|
||||
const addMode = (fitOnAddWidget && fitOnAddWidget.value) ? 'fit' : 'center';
|
||||
const addMode = getImageAddMode(this.canvas.node.widgets);
|
||||
|
||||
// Add all images from the batch as separate layers
|
||||
await this.addBatchImages(
|
||||
@@ -639,9 +639,7 @@ export class CanvasIO {
|
||||
const maskImg = await loadImage(maskCanvas.toDataURL());
|
||||
|
||||
// Respect fit_on_add (scale to output area)
|
||||
const widgets = this.canvas.node.widgets;
|
||||
const fitOnAddWidget = widgets ? widgets.find((w: any) => w.name === "fit_on_add") : null;
|
||||
const shouldFit = fitOnAddWidget && fitOnAddWidget.value;
|
||||
const shouldFit = isFitOnAddEnabled(this.canvas.node.widgets);
|
||||
|
||||
let finalMaskImg: HTMLImageElement = maskImg;
|
||||
if (shouldFit) {
|
||||
@@ -761,9 +759,7 @@ export class CanvasIO {
|
||||
this.canvas.inputDataLoaded = true;
|
||||
|
||||
// Determine add mode based on fit_on_add setting
|
||||
const widgets = this.canvas.node.widgets;
|
||||
const fitOnAddWidget = widgets ? widgets.find((w: any) => w.name === "fit_on_add") : null;
|
||||
const addMode = (fitOnAddWidget && fitOnAddWidget.value) ? 'fit' : 'center';
|
||||
const addMode = getImageAddMode(this.canvas.node.widgets);
|
||||
|
||||
// Load input image(s) only if image input is actually connected, not already loaded, and allowed
|
||||
if (allowImage && !imageLoaded && hasImageInput) {
|
||||
@@ -813,8 +809,7 @@ export class CanvasIO {
|
||||
const maskImg = await loadImage(inputData.input_mask);
|
||||
|
||||
// Determine if we should fit the mask or use it at original size
|
||||
const fitOnAddWidget2 = this.canvas.node.widgets.find((w) => w.name === "fit_on_add");
|
||||
const shouldFit = fitOnAddWidget2 && fitOnAddWidget2.value;
|
||||
const shouldFit = isFitOnAddEnabled(this.canvas.node.widgets);
|
||||
|
||||
let finalMaskImg: HTMLImageElement = maskImg;
|
||||
if (shouldFit && this.canvas.maskTool) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { createModuleLogger } from "./log_system/log_funcs.js";
|
||||
import { snapToGrid, getSnapAdjustment, isPointInRotatedLayer } from "./utils/CommonUtils.js";
|
||||
import { loadImageFromBlob } from "./utils/ImageUtils.js";
|
||||
import { getImageAddMode } from "./utils/CanvasInputUtils.js";
|
||||
import type { Canvas } from './Canvas';
|
||||
import type { Layer, Point } from './types';
|
||||
|
||||
@@ -1285,8 +1286,7 @@ export class CanvasInteractions {
|
||||
|
||||
async loadDroppedImageFile(file: File, worldCoords: Point): Promise<void> {
|
||||
void loadImageFromBlob(file).then(async img => {
|
||||
const fitOnAddWidget = this.canvas.node.widgets.find((w: any) => w.name === "fit_on_add");
|
||||
const addMode = fitOnAddWidget && fitOnAddWidget.value ? 'fit' : 'center';
|
||||
const addMode = getImageAddMode(this.canvas.node.widgets);
|
||||
|
||||
await this.canvas.canvasLayers.addLayerWithImage(img, {}, addMode);
|
||||
}).catch(error => {
|
||||
|
||||
+3
-4
@@ -21,6 +21,7 @@ import { iconLoader, LAYERFORGE_TOOLS } from "./utils/IconLoader.js";
|
||||
import { exportCanvasImage, type CanvasExportAction } from "./utils/CanvasExportUtils.js";
|
||||
import { getFlattenedCanvasBlob, type CanvasBlobVariant } from "./utils/CanvasBlobUtils.js";
|
||||
import { loadPreviewImage } from "./utils/PreviewUtils.js";
|
||||
import { getImageAddMode } from "./utils/CanvasInputUtils.js";
|
||||
import { fetchMattingModelStatus } from "./utils/MattingUtils.js";
|
||||
import { registerImageInClipspace, startSAMDetectorMonitoring, setupSAMDetectorHook } from "./SAMDetectorIntegration.js";
|
||||
import type { ComfyNode, Layer, AddMode } from './types';
|
||||
@@ -535,8 +536,7 @@ async function createCanvasWidget(node: ComfyNode, widget: any, app: ComfyApp):
|
||||
textContent: "Add Image",
|
||||
title: "Add image from file",
|
||||
onclick: () => {
|
||||
const fitOnAddWidget = node.widgets.find((w) => w.name === "fit_on_add");
|
||||
const addMode: AddMode = fitOnAddWidget && fitOnAddWidget.value ? 'fit' : 'center';
|
||||
const addMode: AddMode = getImageAddMode(node.widgets);
|
||||
const input = document.createElement('input');
|
||||
input.type = 'file';
|
||||
input.accept = 'image/*';
|
||||
@@ -563,8 +563,7 @@ async function createCanvasWidget(node: ComfyNode, widget: any, app: ComfyApp):
|
||||
textContent: "Paste Image",
|
||||
title: "Paste image from clipboard",
|
||||
onclick: () => {
|
||||
const fitOnAddWidget = node.widgets.find((w) => w.name === "fit_on_add");
|
||||
const addMode: AddMode = fitOnAddWidget && fitOnAddWidget.value ? 'fit' : 'center';
|
||||
const addMode: AddMode = getImageAddMode(node.widgets);
|
||||
canvas.canvasLayers.handlePaste(addMode);
|
||||
}
|
||||
}),
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import type { AddMode } from '../types';
|
||||
|
||||
export function isFitOnAddEnabled(
|
||||
widgets?: readonly { name: string; value?: unknown }[]
|
||||
): boolean {
|
||||
const fitOnAddWidget = widgets?.find(widget => widget.name === 'fit_on_add');
|
||||
return Boolean(fitOnAddWidget?.value);
|
||||
}
|
||||
|
||||
export function getImageAddMode(
|
||||
widgets?: readonly { name: string; value?: unknown }[]
|
||||
): Extract<AddMode, 'fit' | 'center'> {
|
||||
return isFitOnAddEnabled(widgets) ? 'fit' : 'center';
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import test from 'node:test';
|
||||
|
||||
import {
|
||||
getImageAddMode,
|
||||
isFitOnAddEnabled,
|
||||
} from '../js/utils/CanvasInputUtils.js';
|
||||
|
||||
const [canvasIOSource, canvasViewSource, interactionsSource, inputUtilsSource] = await Promise.all([
|
||||
readFile(new URL('../src/CanvasIO.ts', import.meta.url), 'utf8'),
|
||||
readFile(new URL('../src/CanvasView.ts', import.meta.url), 'utf8'),
|
||||
readFile(new URL('../src/CanvasInteractions.ts', import.meta.url), 'utf8'),
|
||||
readFile(new URL('../src/utils/CanvasInputUtils.ts', import.meta.url), 'utf8'),
|
||||
]);
|
||||
|
||||
test('image entry points use the shared fit_on_add policy', () => {
|
||||
assert.match(inputUtilsSource, /export function isFitOnAddEnabled\(/);
|
||||
assert.match(inputUtilsSource, /export function getImageAddMode\(/);
|
||||
assert.equal((canvasIOSource.match(/getImageAddMode\(this/g) ?? []).length, 2);
|
||||
assert.equal((canvasIOSource.match(/isFitOnAddEnabled\(this/g) ?? []).length, 2);
|
||||
assert.equal((canvasViewSource.match(/getImageAddMode\(node/g) ?? []).length, 2);
|
||||
assert.equal((interactionsSource.match(/getImageAddMode\(this/g) ?? []).length, 1);
|
||||
assert.doesNotMatch(canvasIOSource, /find\(.*fit_on_add/);
|
||||
assert.doesNotMatch(canvasViewSource, /find\(.*fit_on_add/);
|
||||
assert.doesNotMatch(interactionsSource, /find\(.*fit_on_add/);
|
||||
});
|
||||
|
||||
test('shared fit_on_add helper preserves fit and center behavior', () => {
|
||||
assert.equal(isFitOnAddEnabled([{ name: 'fit_on_add', value: true }]), true);
|
||||
assert.equal(isFitOnAddEnabled([{ name: 'fit_on_add', value: false }]), false);
|
||||
assert.equal(isFitOnAddEnabled([{ name: 'fit_on_add', value: 1 }]), true);
|
||||
assert.equal(isFitOnAddEnabled([{ name: 'other', value: true }]), false);
|
||||
assert.equal(isFitOnAddEnabled(undefined), false);
|
||||
|
||||
assert.equal(getImageAddMode([{ name: 'fit_on_add', value: true }]), 'fit');
|
||||
assert.equal(getImageAddMode([{ name: 'fit_on_add', value: false }]), 'center');
|
||||
assert.equal(getImageAddMode(undefined), 'center');
|
||||
});
|
||||
Reference in New Issue
Block a user