[GH-ISSUE #735] I've made a new feature for the for reaching the most frequently used node faster in client-side, but I don't know where should I PR to. #333

Closed
opened 2026-10-27 21:16:21 +00:00 by bsakaguchi · 3 comments
Owner

Originally created by @liusida on GitHub (Jun 2, 2024).
Original GitHub issue: https://github.com/Comfy-Org/ComfyUI-Manager/issues/735

Description of the new feature:

image

image

The right-click context menu currently only contains a static list of nodes, which often isn't very helpful. To address this, I've implemented a feature that traces the creation of all nodes. When building the right-click context menu, the top three most frequently created nodes will be listed after the 'Search' section.

This functionality is implemented entirely in JavaScript, with the information stored in localStorage.getItem("nodeSelectionFrequency") as a dictionary.

For the proof of concept, I modified litegraph.core.js, adding about 20 lines of code, and it works well.

However, I've noticed that comfyanonymous/ComfyUI rarely modifies this file; it is only updated when changes are made upstream. The original jagenjo/litegraph.js has not been updated for five months, and it appears they might be planning a feature-less rewrite of the code before adding new features.

Considering this, I am wondering if I should implement this feature in ComfyUI-Manager by using injection to modify the behavior of the original JavaScript functionality.

===

FYI, I'll attach the code I've added for a proof-of-concept:

// in LGraph.prototype.add:
        // Everytime created a node, update the frequency
        function generateNodeKey(node) {
            let type = node.type;
            let inputs = node.inputs ? node.inputs.map(input => input.type).join(",") : "";
            let outputs = node.outputs ? node.outputs.map(output => output.type).join(",") : "";
            return `${type}|${inputs}|${outputs}`;
        }

        var selectionFrequency = JSON.parse(localStorage.getItem("nodeSelectionFrequency") || "{}");
        let key = generateNodeKey(node);

        if (!selectionFrequency[key]) {
            selectionFrequency[key] = 0;
        }
        selectionFrequency[key] += 1;
        localStorage.setItem("nodeSelectionFrequency", JSON.stringify(selectionFrequency));
// in LGraphCanvas.prototype.showConnectionMenu:
        function findTopFrequentNodes(isFrom, fromSlotType, topN = 3) {
            var selectionFrequency = JSON.parse(localStorage.getItem("nodeSelectionFrequency") || "{}");
            let candidates = [];
        
            for (let key in selectionFrequency) {
                let [type, inputs, outputs] = key.split("|");
                if (isFrom ? inputs.includes(fromSlotType) : outputs.includes(fromSlotType)) {
                    candidates.push({ type: type, frequency: selectionFrequency[key] });
                }
            }
        
            // Sort candidates by frequency in descending order
            candidates.sort((a, b) => b.frequency - a.frequency);
        
            // Return the types of the top N candidates
            return candidates.slice(0, topN).map(c => c.type);
        }        

		// get defaults nodes for this slottype
		var fromSlotType = slotX.type==LiteGraph.EVENT?"_event_":slotX.type;

        var topFrequentNodes = findTopFrequentNodes(isFrom, fromSlotType, 3);  // Get top 3 frequent nodes
        if (topFrequentNodes.length) {
            topFrequentNodes.forEach(nodeType => {
                options.push(nodeType);
            });
            options.push(null);
        }

Originally created by @liusida on GitHub (Jun 2, 2024). Original GitHub issue: https://github.com/Comfy-Org/ComfyUI-Manager/issues/735 Description of the new feature: ![image](https://github.com/ltdrdata/ComfyUI-Manager/assets/14249458/934f87f0-f617-41bd-b13e-131c5dc71730) ![image](https://github.com/ltdrdata/ComfyUI-Manager/assets/14249458/c613d7f7-c662-4544-b9d0-fe83229b90fb) The right-click context menu currently only contains a static list of nodes, which often isn't very helpful. To address this, I've implemented a feature that traces the creation of all nodes. When building the right-click context menu, the top three most frequently created nodes will be listed after the 'Search' section. This functionality is implemented entirely in JavaScript, with the information stored in localStorage.getItem("nodeSelectionFrequency") as a dictionary. For the proof of concept, I modified litegraph.core.js, adding about 20 lines of code, and it works well. However, I've noticed that comfyanonymous/ComfyUI rarely modifies this file; it is only updated when changes are made upstream. The original jagenjo/litegraph.js has not been updated for five months, and it appears they might be planning a feature-less rewrite of the code before adding new features. Considering this, I am wondering if I should implement this feature in ComfyUI-Manager by using injection to modify the behavior of the original JavaScript functionality. === FYI, I'll attach the code I've added for a proof-of-concept: ```js // in LGraph.prototype.add: // Everytime created a node, update the frequency function generateNodeKey(node) { let type = node.type; let inputs = node.inputs ? node.inputs.map(input => input.type).join(",") : ""; let outputs = node.outputs ? node.outputs.map(output => output.type).join(",") : ""; return `${type}|${inputs}|${outputs}`; } var selectionFrequency = JSON.parse(localStorage.getItem("nodeSelectionFrequency") || "{}"); let key = generateNodeKey(node); if (!selectionFrequency[key]) { selectionFrequency[key] = 0; } selectionFrequency[key] += 1; localStorage.setItem("nodeSelectionFrequency", JSON.stringify(selectionFrequency)); // in LGraphCanvas.prototype.showConnectionMenu: function findTopFrequentNodes(isFrom, fromSlotType, topN = 3) { var selectionFrequency = JSON.parse(localStorage.getItem("nodeSelectionFrequency") || "{}"); let candidates = []; for (let key in selectionFrequency) { let [type, inputs, outputs] = key.split("|"); if (isFrom ? inputs.includes(fromSlotType) : outputs.includes(fromSlotType)) { candidates.push({ type: type, frequency: selectionFrequency[key] }); } } // Sort candidates by frequency in descending order candidates.sort((a, b) => b.frequency - a.frequency); // Return the types of the top N candidates return candidates.slice(0, topN).map(c => c.type); } // get defaults nodes for this slottype var fromSlotType = slotX.type==LiteGraph.EVENT?"_event_":slotX.type; var topFrequentNodes = findTopFrequentNodes(isFrom, fromSlotType, 3); // Get top 3 frequent nodes if (topFrequentNodes.length) { topFrequentNodes.forEach(nodeType => { options.push(nodeType); }); options.push(null); } ```
Author
Owner

@atlasan commented on GitHub (Jun 2, 2024):

Nice feature!
I would try to push those to LiteGraph.slot_types_default_out and see if that is enough. ^_^

Regarding this and many other features implementation, I'm currently working on a solution to callbacks that should enable multi extensions to works together allowing to register and return data for the same event without overriding the original (and others) behaviors. Stay tuned.

<!-- gh-comment-id:2143972539 --> @atlasan commented on GitHub (Jun 2, 2024): Nice feature! I would try to push those to `LiteGraph.slot_types_default_out` and see if that is enough. ^_^ Regarding this and many other features implementation, I'm currently working on a solution to callbacks that should enable multi extensions to works together allowing to register and return data for the same event without overriding the original (and others) behaviors. Stay tuned.
Author
Owner

@liusida commented on GitHub (Jun 2, 2024):

Nice feature! I would try to push those to LiteGraph.slot_types_default_out and see if that is enough. ^_^

Regarding this and many other features implementation, I'm currently working on a solution to callbacks that should enable multi extensions to works together allowing to register and return data for the same event without overriding the original (and others) behaviors. Stay tuned.

I see. So I probably should be working on this file:
https://github.com/comfyanonymous/ComfyUI/blob/master/web/extensions/core/slotDefaults.js

And I think I'll need a callback from the creation of nodes to update the default list, so I should create a static function LGraph.onNodeAdded(node) to get the callback... Let me try it out. Thanks.

<!-- gh-comment-id:2144064632 --> @liusida commented on GitHub (Jun 2, 2024): > Nice feature! I would try to push those to `LiteGraph.slot_types_default_out` and see if that is enough. ^_^ > > Regarding this and many other features implementation, I'm currently working on a solution to callbacks that should enable multi extensions to works together allowing to register and return data for the same event without overriding the original (and others) behaviors. Stay tuned. I see. So I probably should be working on this file: https://github.com/comfyanonymous/ComfyUI/blob/master/web/extensions/core/slotDefaults.js And I think I'll need a callback from the creation of nodes to update the default list, so I should create a static function `LGraph.onNodeAdded(node)` to get the callback... Let me try it out. Thanks.
Author
Owner

@ltdrdata commented on GitHub (Nov 18, 2024):

I missed this issue, so my response is quite late.
It’s probably already resolved, but PRs related to the front-end should now be submitted to the front repository.

https://github.com/Comfy-Org/ComfyUI_frontend

<!-- gh-comment-id:2481933237 --> @ltdrdata commented on GitHub (Nov 18, 2024): I missed this issue, so my response is quite late. It’s probably already resolved, but PRs related to the front-end should now be submitted to the front repository. https://github.com/Comfy-Org/ComfyUI_frontend
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Comfy-Org/ComfyUI-Manager#333
No description provided.