diff --git a/README.md b/README.md index 77ba8b4..be7b128 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,14 @@ # ComfyUI String Tools これは [ComfyUI](https://github.com/comfyanonymous/ComfyUI) 用のカスタムノードです。 +単純な文字列ノードである `StringToolsString` ノードと複数行の文字列ノードである `StringToolsText`、 複数のテキストを結合する `StringToolsConcat` ノードと、複数のテキストからランダムにひとつを選択する `StringToolsRandomChoice` ノードを提供します。 +## なぜ単純なテキストノードをこのカスタムノードで提供しているのか + +`StringToolsConcat` ノードと `StringToolsRandomChoice` ノードは文字列を受け付けますが、現状 `PrimitiveNode` は受け付けられません。 +そこで単純な文字列ノードである `StringToolsString` と `StringToolsText` ノードを提供しています。 + ## 機能 ### StringToolsConcat diff --git a/__init__.py b/__init__.py index 2b1ca06..663953d 100644 --- a/__init__.py +++ b/__init__.py @@ -20,6 +20,52 @@ def sort_kwargs_value(basename, kwargs, separator="_"): ) +class StringToolsString: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "text": ( + "STRING", + {}, + ), + }, + } + + RETURN_TYPES = ("STRING",) + FUNCTION = "process" + CATEGORY = "string-tools" + + def process(self, text): + return (text,) + + +class StringToolsText: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "text": ( + "STRING", + {"multiline": True}, + ), + }, + } + + RETURN_TYPES = ("STRING",) + FUNCTION = "process" + CATEGORY = "string-tools" + + def process(self, text): + return (text,) + + class StringToolsConcat: def __init__(self): pass @@ -86,15 +132,20 @@ class StringToolsRandomChoice: CATEGORY = "string-tools" def process(self, *args, **kwargs): - values = list(kwargs.values()) - if len(values) == 0: - return "" + seed = 0 + if "seed" in kwargs: + seed = kwargs["seed"] + del kwargs["seed"] - random.seed(0) - return (random.choice(values),) + values = sort_kwargs_value("text", kwargs) + random.seed(seed) + choice = random.choice(values) + return (choice,) NODE_CLASS_MAPPINGS = { + "StringToolsString": StringToolsString, + "StringToolsText": StringToolsText, "StringToolsConcat": StringToolsConcat, "StringToolsRandomChoice": StringToolsRandomChoice, } diff --git a/js/index.js b/js/index.js index 221371b..919b13d 100644 --- a/js/index.js +++ b/js/index.js @@ -12,7 +12,7 @@ function createCallback(nodename, basename, inputType) { // remove empty inputs for (let index = this.inputs.length; index--;) { const input = this.inputs[index]; - if (getInputBasename(input) === basename && input.link === null) { + if (getInputBasename(input) === basename && input.link === null && this.removeCancel !== index) { this.removeInput(index); const widgetIndex = this.widgets.findIndex((value) => value.name === input.name); if (widgetIndex != -1) { @@ -42,7 +42,6 @@ function createCallback(nodename, basename, inputType) { // setup widget input.widget = { name: input.name, - computeSize: () => [0, -4], // widgetsInputs.js -> hideWidgets }; setWidgetConfig(input, [inputType, { forceInput: true }]); } @@ -55,6 +54,30 @@ function createCallback(nodename, basename, inputType) { onNodeCreatedOriginal.call(this); app.configuringGraph = tmp; } + this.removeCancel = -1; + const onConnectInputOriginal = this.onConnectInput; + this.onConnectInput = function (targetSlot, type, output, originNode, originSlot) { + let retVal = onConnectInputOriginal ? onConnectInputOriginal.apply(this, arguments) : void 0; + if (originNode.type === "PrimitiveNode") { + return false; + } + this.removeCancel = targetSlot; + return retVal; + }; + const onInputDblClickOriginal = this.onInputDblClick; + this.onInputDblClick = function (slot) { + if (onInputDblClickOriginal) { + const originalCreateNode = LiteGraph.createNode; + LiteGraph.createNode = function (nodeType) { + if (nodeData !== "PrimitiveNode") { + return originalCreateNode.apply(this, arguments); + } + return originalCreateNode.call(this, "StringToolsText"); + }; + onInputDblClickOriginal.call(this, slot); + LiteGraph.createNode = originalCreateNode; + } + }; const onConnectionsChange = this.onConnectionsChange; this.onConnectionsChange = function (type, //(0: ?, 1:input, 2: output ) slotIndex, isConnected, link, //LLink, @@ -66,6 +89,7 @@ function createCallback(nodename, basename, inputType) { return; } updateInputs.call(this); + this.removeCancel = -1; }; this.onAdded = function (graph) { this.tmpWidgets = this.widgets; @@ -78,7 +102,7 @@ function createCallback(nodename, basename, inputType) { } }; // デフォルトの onGraphConfigured は動的なソケットを破壊するので使用しない - const onGraphConfiguredOriginal = this.onGraphConfigured; + const onGraphConfigured = this.onGraphConfigured; this.onGraphConfigured = function () { if (this.tmpWidgets) { this.widgets = this.tmpWidgets.concat(this.widgets); diff --git a/src/index.ts b/src/index.ts index 8003d7e..0bc1100 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,7 @@ import { app } from "/scripts/app.js" import { setWidgetConfig } from "/extensions/core/widgetInputs.js" +declare const LiteGraph: any function createCallback(nodename: string, basename: string, inputType: string) { return async function(nodeType:any, nodeData:any, app:any) { @@ -16,7 +17,7 @@ function createCallback(nodename: string, basename: string, inputType: string) { // remove empty inputs for (let index = this.inputs.length; index--; ) { const input = this.inputs[index] - if (getInputBasename(input) === basename && input.link === null) { + if (getInputBasename(input) === basename && input.link === null && this.removeCancel !== index) { this.removeInput(index) const widgetIndex = (this.widgets as any[]).findIndex((value) => value.name === input.name) if (widgetIndex != -1) { @@ -50,9 +51,8 @@ function createCallback(nodename: string, basename: string, inputType: string) { // setup widget input.widget = { name: input.name, - computeSize: () => [0, -4], // widgetsInputs.js -> hideWidgets } - setWidgetConfig(input, [inputType, {forceInput: true}]); + setWidgetConfig(input, [inputType, {forceInput: true}]) } } @@ -64,6 +64,33 @@ function createCallback(nodename: string, basename: string, inputType: string) { onNodeCreatedOriginal.call(this) app.configuringGraph = tmp } + this.removeCancel = -1 + + const onConnectInputOriginal = this.onConnectInput + this.onConnectInput = function(targetSlot: number, type: string, output:any, originNode:any, originSlot:number) { + let retVal = onConnectInputOriginal ? onConnectInputOriginal.apply(this, arguments) : void 0 + if (originNode.type === "PrimitiveNode") { + return false + } + this.removeCancel = targetSlot + + return retVal + } + + const onInputDblClickOriginal = this.onInputDblClick + this.onInputDblClick = function(slot: number) { + if (onInputDblClickOriginal) { + const originalCreateNode = LiteGraph.createNode + LiteGraph.createNode = function(nodeType: string) { + if (nodeData !== "PrimitiveNode") { + return originalCreateNode.apply(this, arguments) + } + return originalCreateNode.call(this, "StringToolsText") + } + onInputDblClickOriginal.call(this, slot) + LiteGraph.createNode = originalCreateNode + } + } const onConnectionsChange = this.onConnectionsChange this.onConnectionsChange = function( @@ -81,6 +108,8 @@ function createCallback(nodename: string, basename: string, inputType: string) { } updateInputs.call(this) + + this.removeCancel = -1 } this.onAdded = function(graph: any) { @@ -94,7 +123,7 @@ function createCallback(nodename: string, basename: string, inputType: string) { } // デフォルトの onGraphConfigured は動的なソケットを破壊するので使用しない - const onGraphConfiguredOriginal = this.onGraphConfigured + const onGraphConfigured = this.onGraphConfigured this.onGraphConfigured = function() { if (this.tmpWidgets) { this.widgets = this.tmpWidgets.concat(this.widgets)