diff --git a/src/character_select.py b/src/character_select.py index c046b52..18f8fd5 100644 --- a/src/character_select.py +++ b/src/character_select.py @@ -123,7 +123,7 @@ class CharacterSelectNode: @classmethod def INPUT_TYPES(cls): cls.check_initialize() - character_names = ["None"] + list(cls.final_characters) + character_names = ["None"] + list(cls.final_characters) + ["SN0W_CHARACTER_SELECTOR"] return { "required": { "character": (character_names,), diff --git a/web/js/characterNode.js b/web/js/characterNode.js new file mode 100644 index 0000000..ed922e1 --- /dev/null +++ b/web/js/characterNode.js @@ -0,0 +1,50 @@ +import { app } from "../../../scripts/app.js"; + +app.registerExtension({ + name: "sn0w.CustomCharacterNode", + async setup() { + const existingContextMenu = LiteGraph.ContextMenu; + LiteGraph.ContextMenu = function (values, options) { + if (values[values.length - 1] != "SN0W_CHARACTER_SELECTOR") { + return existingContextMenu.call(this, values, options); + } + + const originalCallback = options.callback; + const seriesGroups = {}; + values.slice(1).forEach((value) => { + const match = value.match(/^(.+?)\s*\((.+?)\)$/); + if (match) { + const [, , seriesName] = match; + if (!seriesGroups[seriesName]) { + seriesGroups[seriesName] = []; + } + seriesGroups[seriesName].push(value); + } + }); + + const newCallback = (item, options) => { + if (originalCallback && item && item.content) { + originalCallback(item.content, options); + } + }; + + const hierarchicalValues = [{ content: "None", disabled: false, callback: newCallback }]; + Object.keys(seriesGroups).forEach((seriesName) => { + hierarchicalValues.push({ + content: seriesName, + disabled: false, + has_submenu: true, + submenu: { + options: seriesGroups[seriesName].map((characterName) => ({ + content: characterName, + callback: newCallback, + })), + }, + }); + }); + + options.callback = undefined; + existingContextMenu.call(this, hierarchicalValues, options); + }; + }, +});