diff --git a/py/rgthree_server.py b/py/rgthree_server.py index 9d0cb38..f9af2d3 100644 --- a/py/rgthree_server.py +++ b/py/rgthree_server.py @@ -14,30 +14,44 @@ DIR_WEB = os.path.abspath(f'{THIS_DIR}/../web/') routes = PromptServer.instance.routes +def set_default_page_resources(path): + """ Sets up routes for handling static files under a path.""" + + @routes.get(f'/rgthree/{path}/{{file}}') + async def get_resource(request): + """ Returns a resource file. """ + return web.FileResponse(os.path.join(DIR_WEB, path, request.match_info['file'])) + + @routes.get(f'/rgthree/{path}/{{subdir}}/{{file}}') + async def get_resource_subdir(request): + """ Returns a resource file. """ + return web.FileResponse( + os.path.join(DIR_WEB, path, request.match_info['subdir'], request.match_info['file'])) + + +def set_default_page_routes(path): + """ Sets default path handling for a hosted rgthree page. """ + + @routes.get(f'/rgthree/{path}') + async def get_path_redir(request): + """ Redirects to the path adding a trailing slash. """ + raise web.HTTPFound(f'{request.path}/') + + @routes.get(f'/rgthree/{path}/') + async def get_path_index(request): + """ Handles the page's index loading. """ + html = '' + with open(os.path.join(DIR_WEB, path, 'index.html'), 'r', encoding='UTF-8') as file: + html = file.read() + return web.Response(text=html, content_type='text/html') + + set_default_page_resources(path) + + # Sometimes other pages (link_fixer, etc.) may want to import JS from the comfyui # directory. To allows TS to resolve like '../comfyui/file.js', we'll also resolve any module HTTP # to these routes. -@routes.get('/rgthree/comfyui/{file}') -async def get_comfyui_file_relative(request): - return web.FileResponse(os.path.join(DIR_WEB, 'comfyui', request.match_info['file'])) +set_default_page_resources("comfyui") +set_default_page_resources("common") -@routes.get('/rgthree/common/{file}') -async def get_common_file(request): - return web.FileResponse(os.path.join(DIR_WEB, 'common', request.match_info['file'])) - - -@routes.get('/rgthree/link_fixer') -async def link_fixer_home_redir(request): - raise web.HTTPFound(f'{request.path}/') - -@routes.get('/rgthree/link_fixer/') -async def link_fixer_home(request): - html = '' - with open(os.path.join(DIR_WEB, 'link_fixer', 'index.html'), 'r', encoding='UTF-8') as file: - html = file.read() - return web.Response(text=html, content_type='text/html') - - -@routes.get('/rgthree/link_fixer/{file}') -async def get_link_fixer_file(request): - return web.FileResponse(os.path.join(DIR_WEB, 'link_fixer', request.match_info['file'])) +set_default_page_routes("link_fixer") diff --git a/src_web/comfyui/reroute.ts b/src_web/comfyui/reroute.ts index d4881a1..618e130 100644 --- a/src_web/comfyui/reroute.ts +++ b/src_web/comfyui/reroute.ts @@ -1,8 +1,12 @@ // / // @ts-ignore import { app } from "../../scripts/app.js"; -// @ts-ignore -import { getWidgetConfig, mergeIfValid, setWidgetConfig } from "../../extensions/core/widgetInputs.js"; +import { + getWidgetConfig, + mergeIfValid, + setWidgetConfig, + // @ts-ignore +} from "../../extensions/core/widgetInputs.js"; // @ts-ignore import { rgthreeConfig } from "./rgthree_config.js"; import { rgthree } from "./rgthree.js"; @@ -31,6 +35,7 @@ import { } from "./utils.js"; import { wait } from "rgthree/common/shared_utils.js"; import { RgthreeBaseNode } from "./base_node.js"; +import { NodeTypesString } from "./constants.js"; declare const LiteGraph: typeof TLiteGraph; declare const LGraphNode: typeof TLGraphNode; @@ -239,27 +244,24 @@ class RerouteService { // To the right, and further right than up or down. layout[0] = canvas.connecting_output ? "Left" : "Right"; layout[1] = LAYOUT_LABEL_OPPOSITES[layout[0]]!; - node.pos[0] -= (node.size[0] + 10); - node.pos[1] -= (Math.round((node.size[1] / 2) / 10) * 10); - + node.pos[0] -= node.size[0] + 10; + node.pos[1] -= Math.round(node.size[1] / 2 / 10) * 10; } else if (distX < 0 && Math.abs(distX) > Math.abs(distY)) { // To the left, and further right than up or down. layout[0] = canvas.connecting_output ? "Right" : "Left"; layout[1] = LAYOUT_LABEL_OPPOSITES[layout[0]]!; - node.pos[1] -= (Math.round((node.size[1] / 2) / 10) * 10); - + node.pos[1] -= Math.round(node.size[1] / 2 / 10) * 10; } else if (distY < 0 && Math.abs(distY) > Math.abs(distX)) { // Above and further above than left or right. layout[0] = canvas.connecting_output ? "Bottom" : "Top"; layout[1] = LAYOUT_LABEL_OPPOSITES[layout[0]]!; - node.pos[0] -= (Math.round((node.size[0] / 2) / 10) * 10); - + node.pos[0] -= Math.round(node.size[0] / 2 / 10) * 10; } else if (distY > 0 && Math.abs(distY) > Math.abs(distX)) { // Below and further below than left or right. layout[0] = canvas.connecting_output ? "Top" : "Bottom"; layout[1] = LAYOUT_LABEL_OPPOSITES[layout[0]]!; - node.pos[0] -= (Math.round((node.size[0] / 2) / 10) * 10); - node.pos[1] -= (node.size[1] + 10); + node.pos[0] -= Math.round(node.size[0] / 2 / 10) * 10; + node.pos[1] -= node.size[1] + 10; } setConnectionsLayout(entry.node, layout); @@ -329,8 +331,7 @@ const SERVICE = new RerouteService(); * The famous ReroutNode, that has true multidirectional, expansive sizes, etc. */ class RerouteNode extends RgthreeBaseNode { - - static override title = "Reroute (rgthree)"; + static override title = NodeTypesString.REROUTE; static readonly title_mode = LiteGraph.NO_TITLE; @@ -383,6 +384,16 @@ class RerouteNode extends RgthreeBaseNode { this.configuring = false; } + /** Finds the input slot; since we only ever have one, this is always 0. */ + override findInputSlot(name: string): number { + return 0; + } + + /** Finds the output slot; since we only ever have one, this is always 0. */ + override findOutputSlot(name: string): number { + return 0; + } + setResizable(resizable: boolean) { this.properties["resizable"] = !!resizable; this.resizable = this.properties["resizable"]; @@ -585,16 +596,20 @@ class RerouteNode extends RgthreeBaseNode { outputWidgetConfig = config[1] ?? {}; outputType = config[0]; if (!outputWidget) { - outputWidget = outputNode.widgets?.find((w) => w.name === output?.widget?.name); + outputWidget = outputNode.widgets?.find( + (w) => w.name === output?.widget?.name, + ); } const merged = mergeIfValid(output, [config[0], outputWidgetConfig]); if (merged.customConfig) { outputWidgetConfig = merged.customConfig; } } - } catch(e) { + } catch (e) { // Something happened, probably because comfyUI changes their methods. - console.error('[rgthree] Could not propagate widget infor for reroute; maybe ComfyUI updated?'); + console.error( + "[rgthree] Could not propagate widget infor for reroute; maybe ComfyUI updated?", + ); outputWidgetConfig = null; outputWidget = null; } @@ -631,13 +646,17 @@ class RerouteNode extends RgthreeBaseNode { // For primitive nodes, which look at the widget to dsplay themselves. if (outputWidgetConfig && outputWidget && outputType) { node.inputs[0]!.widget = { name: "value" }; - setWidgetConfig(node.inputs[0], [outputType ?? displayType, outputWidgetConfig], outputWidget); + setWidgetConfig( + node.inputs[0], + [outputType ?? displayType, outputWidgetConfig], + outputWidget, + ); } else { setWidgetConfig(node.inputs[0], null); } - } catch(e) { + } catch (e) { // Something happened, probably because comfyUI changes their methods. - console.error('[rgthree] Could not set widget config for reroute; maybe ComfyUI updated?'); + console.error("[rgthree] Could not set widget config for reroute; maybe ComfyUI updated?"); outputWidgetConfig = null; outputWidget = null; if (node.inputs[0]?.widget) { @@ -966,11 +985,10 @@ class RerouteNode extends RgthreeBaseNode { shortcuts to bring your workflow to the next level.

- ${!CONFIG_FAST_REROUTE_ENABLED - ? - `

Fast Shortcuts are currently disabled.` - : - ` + ${ + !CONFIG_FAST_REROUTE_ENABLED + ? `

Fast Shortcuts are currently disabled.` + : `

- `} + ` + }

- To change, ${!CONFIG_FAST_REROUTE_ENABLED ? 'enable':'disable'} or configure sohrtcuts, + To change, ${!CONFIG_FAST_REROUTE_ENABLED ? "enable" : "disable"} or configure sohrtcuts, make a copy of /custom_nodes/rgthree-comfy/rgthree_config.json.default to /custom_nodes/rgthree-comfy/rgthree_config.json and configure under diff --git a/src_web/comfyui/rgthree.css b/src_web/comfyui/rgthree.css index 33a24ae..e1f3b10 100644 --- a/src_web/comfyui/rgthree.css +++ b/src_web/comfyui/rgthree.css @@ -193,3 +193,30 @@ margin: 0.25rem; cursor: pointer; } + +.litegraph.litecontextmenu .litemenu-entry.rgthree-contextmenu-item { + display: flex; + flex-direction: row; + align-items: center; + justify-content: start; +} +.litegraph.litecontextmenu .litemenu-entry.rgthree-contextmenu-item svg { + fill: currentColor; + width: auto; + height: 16px; + margin-right: 6px; +} +.litegraph.litecontextmenu .litemenu-entry.rgthree-contextmenu-item svg.github-star { + fill: rgb(227, 179, 65); +} + +.litegraph.litecontextmenu .litemenu-entry.rgthree-contextmenu-label { + color: #dde; + background-color: #090909 !important; + margin: 0; + padding: 2px; + cursor: default; + opacity: 1; + padding: 4px; + font-weight: bold; +} \ No newline at end of file diff --git a/src_web/comfyui/rgthree.ts b/src_web/comfyui/rgthree.ts index de0d323..34d9526 100644 --- a/src_web/comfyui/rgthree.ts +++ b/src_web/comfyui/rgthree.ts @@ -12,7 +12,8 @@ import { api } from "../../scripts/api.js"; import { rgthreeConfig } from "./rgthree_config.js"; import { fixBadLinks } from "rgthree/common/link_fixer.js"; import { wait } from "rgthree/common/shared_utils.js"; -import { waitForCanvas, waitForGraph } from "./utils.js"; +import { replaceNode, waitForCanvas, waitForGraph } from "./utils.js"; +import { NodeTypesString } from "./constants.js"; declare const LGraphCanvas: typeof TLGraphCanvas; @@ -139,6 +140,7 @@ class Rgthree extends EventTarget { this.initializeGraphAndCanvasHooks(); this.initializeComfyUIHooks(); + this.initializeContextMenu(); wait(100).then(() => { this.injectRgthreeCss(); @@ -193,6 +195,81 @@ class Rgthree extends EventTarget { }; } + /** + * Initializes hooks specific to an rgthree-comfy context menu on the root menu. + */ + private async initializeContextMenu() { + const graph = await waitForGraph(); + setTimeout(() => { + const getCanvasMenuOptions = LGraphCanvas.prototype.getCanvasMenuOptions; + LGraphCanvas.prototype.getCanvasMenuOptions = function (...args: any[]) { + const options = getCanvasMenuOptions.apply(this, [...args] as any); + + const hasReroutes = graph._nodes.find((n) => n.type == "Reroute"); + + options.push(null); // Divider + options.push({ + content: ` + + + + + + + rgthree-comfy`, + className: "rgthree-contextmenu-item rgthree-contextmenu-main-rgthree-comfy", + submenu: { + options: [ + { + content: "Functions", + disabled: true, + className: "rgthree-contextmenu-item rgthree-contextmenu-label", + }, + { + content: "Convert all 'Reroute' => 'Reroute (rgthree)'", + disabled: !hasReroutes, + callback: (...args: any[]) => { + const msg = + "Convert all comfyui reroute nodes to rgthree-comfy reroute nodes? \n" + + "(First save a copy of your workflow & check reroute connections afterwards)"; + if (!window.confirm(msg)) { + return; + } + (async () => { + const nodes = [...graph._nodes]; + for (const node of nodes) { + if (node.type == "Reroute") { + await replaceNode(node, NodeTypesString.REROUTE); + } + } + })(); + }, + }, + { + content: "More...", + disabled: true, + className: "rgthree-contextmenu-item rgthree-contextmenu-label", + }, + { + content: ` + + + + Star on Github`, + callback: (...args: any[]) => { + window.open("https://github.com/rgthree/rgthree-comfy", "_blank"); + }, + className: "rgthree-contextmenu-item rgthree-contextmenu-github", + }, + ], + }, + }); + + return options; + }; + }, 1000); + } + /** * Initialize a bunch of hooks into ComfyUI and/or LiteGraph itself so we can either keep state or * context on what's happening so nodes can respond appropriately. This is usually to fix broken diff --git a/src_web/common/media/rgthree.svg b/src_web/common/media/rgthree.svg new file mode 100644 index 0000000..ee32775 --- /dev/null +++ b/src_web/common/media/rgthree.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/web/comfyui/reroute.js b/web/comfyui/reroute.js index b282191..cc081e1 100644 --- a/web/comfyui/reroute.js +++ b/web/comfyui/reroute.js @@ -1,11 +1,12 @@ var _a, _b; import { app } from "../../scripts/app.js"; -import { getWidgetConfig, mergeIfValid, setWidgetConfig } from "../../extensions/core/widgetInputs.js"; +import { getWidgetConfig, mergeIfValid, setWidgetConfig, } from "../../extensions/core/widgetInputs.js"; import { rgthreeConfig } from "./rgthree_config.js"; import { rgthree } from "./rgthree.js"; import { IoDirection, LAYOUT_CLOCKWISE, LAYOUT_LABEL_OPPOSITES, LAYOUT_LABEL_TO_DATA, addConnectionLayoutSupport, addMenuItem, getSlotLinks, isValidConnection, setConnectionsLayout, waitForCanvas, } from "./utils.js"; import { wait } from "../../rgthree/common/shared_utils.js"; import { RgthreeBaseNode } from "./base_node.js"; +import { NodeTypesString } from "./constants.js"; const CONFIG_REROUTE = ((_a = rgthreeConfig === null || rgthreeConfig === void 0 ? void 0 : rgthreeConfig["nodes"]) === null || _a === void 0 ? void 0 : _a["reroute"]) || {}; const CONFIG_FAST_REROUTE = CONFIG_REROUTE["fast_reroute"]; const CONFIG_FAST_REROUTE_ENABLED = (_b = CONFIG_FAST_REROUTE["enabled"]) !== null && _b !== void 0 ? _b : false; @@ -126,24 +127,24 @@ class RerouteService { if (distX > 0 && Math.abs(distX) > Math.abs(distY)) { layout[0] = canvas.connecting_output ? "Left" : "Right"; layout[1] = LAYOUT_LABEL_OPPOSITES[layout[0]]; - node.pos[0] -= (node.size[0] + 10); - node.pos[1] -= (Math.round((node.size[1] / 2) / 10) * 10); + node.pos[0] -= node.size[0] + 10; + node.pos[1] -= Math.round(node.size[1] / 2 / 10) * 10; } else if (distX < 0 && Math.abs(distX) > Math.abs(distY)) { layout[0] = canvas.connecting_output ? "Right" : "Left"; layout[1] = LAYOUT_LABEL_OPPOSITES[layout[0]]; - node.pos[1] -= (Math.round((node.size[1] / 2) / 10) * 10); + node.pos[1] -= Math.round(node.size[1] / 2 / 10) * 10; } else if (distY < 0 && Math.abs(distY) > Math.abs(distX)) { layout[0] = canvas.connecting_output ? "Bottom" : "Top"; layout[1] = LAYOUT_LABEL_OPPOSITES[layout[0]]; - node.pos[0] -= (Math.round((node.size[0] / 2) / 10) * 10); + node.pos[0] -= Math.round(node.size[0] / 2 / 10) * 10; } else if (distY > 0 && Math.abs(distY) > Math.abs(distX)) { layout[0] = canvas.connecting_output ? "Top" : "Bottom"; layout[1] = LAYOUT_LABEL_OPPOSITES[layout[0]]; - node.pos[0] -= (Math.round((node.size[0] / 2) / 10) * 10); - node.pos[1] -= (node.size[1] + 10); + node.pos[0] -= Math.round(node.size[0] / 2 / 10) * 10; + node.pos[1] -= node.size[1] + 10; } setConnectionsLayout(entry.node, layout); if (canvas.connecting_output) { @@ -235,6 +236,12 @@ class RerouteNode extends RgthreeBaseNode { this.applyNodeSize(); this.configuring = false; } + findInputSlot(name) { + return 0; + } + findOutputSlot(name) { + return 0; + } setResizable(resizable) { this.properties["resizable"] = !!resizable; this.resizable = this.properties["resizable"]; @@ -399,7 +406,7 @@ class RerouteNode extends RgthreeBaseNode { } } catch (e) { - console.error('[rgthree] Could not propagate widget infor for reroute; maybe ComfyUI updated?'); + console.error("[rgthree] Could not propagate widget infor for reroute; maybe ComfyUI updated?"); outputWidgetConfig = null; outputWidget = null; } @@ -435,7 +442,7 @@ class RerouteNode extends RgthreeBaseNode { } } catch (e) { - console.error('[rgthree] Could not set widget config for reroute; maybe ComfyUI updated?'); + console.error("[rgthree] Could not set widget config for reroute; maybe ComfyUI updated?"); outputWidgetConfig = null; outputWidget = null; if ((_j = node.inputs[0]) === null || _j === void 0 ? void 0 : _j.widget) { @@ -712,10 +719,8 @@ class RerouteNode extends RgthreeBaseNode {

${!CONFIG_FAST_REROUTE_ENABLED - ? - `

Fast Shortcuts are currently disabled.` - : - ` + ? `

Fast Shortcuts are currently disabled.` + : `

  • ${CONFIG_KEY_CREATE_WHILE_LINKING} Create a new reroute node while dragging @@ -744,7 +749,7 @@ class RerouteNode extends RgthreeBaseNode {

`}

- To change, ${!CONFIG_FAST_REROUTE_ENABLED ? 'enable' : 'disable'} or configure sohrtcuts, + To change, ${!CONFIG_FAST_REROUTE_ENABLED ? "enable" : "disable"} or configure sohrtcuts, make a copy of /custom_nodes/rgthree-comfy/rgthree_config.json.default to /custom_nodes/rgthree-comfy/rgthree_config.json and configure under @@ -753,7 +758,7 @@ class RerouteNode extends RgthreeBaseNode { `; } } -RerouteNode.title = "Reroute (rgthree)"; +RerouteNode.title = NodeTypesString.REROUTE; RerouteNode.title_mode = LiteGraph.NO_TITLE; RerouteNode.collapsable = false; RerouteNode.layout_slot_offset = 5; diff --git a/web/comfyui/rgthree.css b/web/comfyui/rgthree.css index 33a24ae..e1f3b10 100644 --- a/web/comfyui/rgthree.css +++ b/web/comfyui/rgthree.css @@ -193,3 +193,30 @@ margin: 0.25rem; cursor: pointer; } + +.litegraph.litecontextmenu .litemenu-entry.rgthree-contextmenu-item { + display: flex; + flex-direction: row; + align-items: center; + justify-content: start; +} +.litegraph.litecontextmenu .litemenu-entry.rgthree-contextmenu-item svg { + fill: currentColor; + width: auto; + height: 16px; + margin-right: 6px; +} +.litegraph.litecontextmenu .litemenu-entry.rgthree-contextmenu-item svg.github-star { + fill: rgb(227, 179, 65); +} + +.litegraph.litecontextmenu .litemenu-entry.rgthree-contextmenu-label { + color: #dde; + background-color: #090909 !important; + margin: 0; + padding: 2px; + cursor: default; + opacity: 1; + padding: 4px; + font-weight: bold; +} \ No newline at end of file diff --git a/web/comfyui/rgthree.js b/web/comfyui/rgthree.js index 20005b5..33b765d 100644 --- a/web/comfyui/rgthree.js +++ b/web/comfyui/rgthree.js @@ -3,7 +3,8 @@ import { api } from "../../scripts/api.js"; import { rgthreeConfig } from "./rgthree_config.js"; import { fixBadLinks } from "../../rgthree/common/link_fixer.js"; import { wait } from "../../rgthree/common/shared_utils.js"; -import { waitForCanvas, waitForGraph } from "./utils.js"; +import { replaceNode, waitForCanvas, waitForGraph } from "./utils.js"; +import { NodeTypesString } from "./constants.js"; export var LogLevel; (function (LogLevel) { LogLevel[LogLevel["IMPORTANT"] = 1] = "IMPORTANT"; @@ -86,6 +87,7 @@ class Rgthree extends EventTarget { }); this.initializeGraphAndCanvasHooks(); this.initializeComfyUIHooks(); + this.initializeContextMenu(); wait(100).then(() => { this.injectRgthreeCss(); }); @@ -113,6 +115,73 @@ class Rgthree extends EventTarget { LGraphCanvas.onShowPropertyEditor({}, null, null, null, graph._groups[graph._groups.length - 1]); }; } + async initializeContextMenu() { + const graph = await waitForGraph(); + setTimeout(() => { + const getCanvasMenuOptions = LGraphCanvas.prototype.getCanvasMenuOptions; + LGraphCanvas.prototype.getCanvasMenuOptions = function (...args) { + const options = getCanvasMenuOptions.apply(this, [...args]); + const hasReroutes = graph._nodes.find((n) => n.type == "Reroute"); + options.push(null); + options.push({ + content: ` + + + + + + + rgthree-comfy`, + className: "rgthree-contextmenu-item rgthree-contextmenu-main-rgthree-comfy", + submenu: { + options: [ + { + content: "Functions", + disabled: true, + className: "rgthree-contextmenu-item rgthree-contextmenu-label", + }, + { + content: "Convert all 'Reroute' => 'Reroute (rgthree)'", + disabled: !hasReroutes, + callback: (...args) => { + const msg = "Convert all comfyui reroute nodes to rgthree-comfy reroute nodes? \n" + + "(First save a copy of your workflow & check reroute connections afterwards)"; + if (!window.confirm(msg)) { + return; + } + (async () => { + const nodes = [...graph._nodes]; + for (const node of nodes) { + if (node.type == "Reroute") { + await replaceNode(node, NodeTypesString.REROUTE); + } + } + })(); + }, + }, + { + content: "More...", + disabled: true, + className: "rgthree-contextmenu-item rgthree-contextmenu-label", + }, + { + content: ` + + + + Star on Github`, + callback: (...args) => { + window.open("https://github.com/rgthree/rgthree-comfy", "_blank"); + }, + className: "rgthree-contextmenu-item rgthree-contextmenu-github", + }, + ], + }, + }); + return options; + }; + }, 1000); + } initializeComfyUIHooks() { const rgthree = this; const queuePrompt = app.queuePrompt; diff --git a/web/common/media/rgthree.svg b/web/common/media/rgthree.svg new file mode 100644 index 0000000..ee32775 --- /dev/null +++ b/web/common/media/rgthree.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file