From 9d5350cd06434092e4baa33e52616433b0940dd4 Mon Sep 17 00:00:00 2001 From: Nuked Date: Tue, 4 Jun 2024 22:35:52 +0200 Subject: [PATCH] - Fixed some bugs - Changed icon bar positioning to the border of the screen - Implemented the DAMN subcategory view on the node panel!!! xD (you can disable it in the settings if you don't want it) - Added the ability to disable the "Auto Show" feature - Added the ability to view folders inside the workflow panel - Added some tooltips for better UX - Changed the Custom Node Categories icon (I didn't like the old one) - Added a little icon in custom categories and folders/subcategories - Added the ability to move workflows in custom categories via the context menu - Added the ability to open workflows in another TAB --- .gitignore | 1 + README.md | 28 + __init__.py | 85 ++- app/css/base_style.css | 56 +- app/html/sidebar.html | 27 +- app/js/functions/settings.js | 50 +- app/js/functions/utils.js | 63 ++- app/js/sidebar_node.js | 325 +++++++----- .../custom_categories/custom_categories.html | 4 +- .../custom_categories/custom_categories.jsb | 35 +- app/panels/custom_categories/style.css | 1 + .../custom_templates/custom_templates.html | 6 +- .../custom_templates/custom_templates.jsb | 20 +- app/panels/custom_templates/style.css | 1 + .../custom_workflows/custom_workflows.html | 7 +- .../custom_workflows/custom_workflows.jsb | 482 ++++++++++++++---- app/panels/custom_workflows/style.css | 29 ++ app/views/views.json | 6 +- pyproject.toml | 2 +- 19 files changed, 884 insertions(+), 344 deletions(-) diff --git a/.gitignore b/.gitignore index 38931e8..ebd586d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ # Config file app/settings.json +app/settings.json.bak # Byte-compiled / optimized / DLL files __pycache__/ diff --git a/README.md b/README.md index 8dfb423..7062f73 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,25 @@ For what i know nobody did it, so i did it. Maybe you don't need it. I need it >.< # Updates + +- 04-06-2024 + - Fixed some bugs + - Changed icon bar positioning to the border of the screen + - Implemented the DAMN subcategory view on the node panel!!! xD (you can disable it in the settings if you don't want it) + - Added the ability to disable the "Auto Show" feature + - Added the ability to view folders inside the workflow panel + - Added some tooltips for better UX + - Changed the Custom Node Categories icon (I didn't like the old one) + - Added a little icon in custom categories and folders/subcategories + - Added the ability to move workflows in custom categories via the context menu + - Added the ability to open workflows in another TAB + +NOTE: If you had some custom categories in the workflow panel, at the first start they will be migrated to the new system (since I did a full rewrite to support the folders). It is possible that you need to refresh the page more than once to see them again. Also, a backup of the settings.json will be created before the migration in case anything goes wrong. + +NOTE2: I hope I did not break anything T_T + + + - 25-05-2024 - Fixed some bugs - Added Templates support** @@ -66,6 +85,11 @@ NOTE: If you choose to use a different method to install the ComfyUI-N-Sidebar, 1. Navigate to the cloned repo e.g. `custom_nodes/ComfyUI-N-Sidebar` 2. `git pull` + +# Settings +The most important settings are stored in `custom_nodes/ComfyUI-N-Sidebar/settings.json` + + # Keyboard Shortcuts - `Alt+Z` to toggle show/hide sidebar @@ -129,6 +153,10 @@ PS: Workflow Path - [x] Color integration with Jovimetrix - [x] Better search - [x] Custom Categories!! +- [x] Workflows +- [x] Templates +- [ ] Custom Shortcuts +- [ ] Export and Import Settings diff --git a/__init__.py b/__init__.py index 0341591..8e169f7 100644 --- a/__init__.py +++ b/__init__.py @@ -5,6 +5,8 @@ import os import json from collections import OrderedDict from pathlib import Path +import base64 +import shutil @@ -53,6 +55,55 @@ class JSONConfigManager: self.data[parameter_name] = value self.write_json_file() +def generate_id(file_path): + + encoded_bytes = base64.b64encode(file_path.encode('utf-8')) + encoded_str = str(encoded_bytes, 'utf-8') + return encoded_str + + +""" +def scan_directory(directory, workflows_dict, existing_names, main_path = ''): + for entry in os.scandir(directory): + if entry.is_file() and entry.name.endswith('.json'): + base_name = entry.name[:-5] # Remove the '.json' extension + #unique_name = base_name + + #if unique_name in workflows_dict: + # folder_name = os.path.basename(directory) + # unique_name = f"{base_name} (from {folder_name})" + + subfolder = entry.path.replace(main_path, '').replace(entry.name, '') + + workflows_dict[base_name] = {"name": base_name,"path": entry.path, "subfolder": subfolder} + existing_names.add(base_name) + elif entry.is_dir(): + scan_directory(entry.path, workflows_dict, existing_names,main_path) + +""" + +def scan_directory(directory, workflows_dict, existing_names, main_path=''): + for entry in os.scandir(directory): + if entry.is_file() and entry.name.endswith('.json'): + base_name = entry.name[:-5] # Remove the '.json' extension + subfolder = entry.path.replace(main_path, '').replace(entry.name, '') + + unique_id = generate_id(entry.path) + workflows_dict[unique_id] = { + "name": base_name, + "path": entry.path, + "subfolder": subfolder + } + existing_names.add(base_name) + elif entry.is_dir(): + scan_directory(entry.path, workflows_dict, existing_names, main_path) + + + + + + + # If is different from ComfyUISidebar, change it if __package__ != "ComfyUI-N-Sidebar": @@ -109,6 +160,21 @@ for folder in folders: list_panels_array.append(folder) +@server.PromptServer.instance.routes.get("/sidebar/backup" ) +async def s_get(request): + backup_path = file_path + ".bak" + + try: + # Crea una copia di settings.json come settings.json.bak + shutil.copyfile(file_path, backup_path) + except Exception as e: + result = {"result": "ERROR", "message": str(e)} + return web.json_response(result, content_type='application/json') + + result = {"result": "OK"} + return web.json_response(result, content_type='application/json') + + @server.PromptServer.instance.routes.get("/sidebar/workflows" ) async def s_get(request): @@ -116,18 +182,9 @@ async def s_get(request): existing_names = set() for list_workflows in workflow_dirs: - for entry in os.scandir(list_workflows): - if entry.is_file() and entry.name.endswith('.json'): - base_name = entry.name[:-5] # Remove the '.json' extension - unique_name = base_name - - if unique_name in workflows_dict: - folder_name = os.path.basename(list_workflows) - unique_name = f"{base_name} (from {folder_name})" - - workflows_dict[unique_name] = entry.path - existing_names.add(unique_name) - sorted_workflows_dict = dict(sorted(workflows_dict.items())) + scan_directory(list_workflows, workflows_dict, existing_names, list_workflows) + + sorted_workflows_dict = dict(sorted(workflows_dict.items(), key=lambda x: x[1]["name"].lower())) return web.json_response(sorted_workflows_dict, content_type='application/json') @@ -153,10 +210,10 @@ async def s_get(request): if not path.exists(): raise FileNotFoundError(f"The system cannot find the path specified: {path}") - # Ottieni la directory e il nuovo percorso new_path = path.with_name(newName).with_suffix('.json') + if new_path.exists(): - result = {"result": "File already exists!"} + result = {"result": "File already exists!"} else: #rename os.rename(path, new_path) diff --git a/app/css/base_style.css b/app/css/base_style.css index 6cbb667..666000f 100644 --- a/app/css/base_style.css +++ b/app/css/base_style.css @@ -25,9 +25,10 @@ padding-left: 5px; padding-right: 5px; - margin-bottom: 8px; + /*margin-bottom: 8px;*/ margin-top: 0; padding-top: 5px; + width: 100%; } .sidebar li { @@ -78,7 +79,7 @@ width: calc(100% - 75px); margin-top: 5px; margin-bottom: 10px; - margin-left: 10px; + margin-left: 44px; z-index: 400; @@ -133,7 +134,10 @@ font-size: 15px; padding-bottom: 8px; } - + .sidebarCategory { + border-bottom:var(--comfy-input-bg) solid 1px; + text-wrap: nowrap; + } #sidebarCustomNodes { list-style-type: none; font-family: 'Open Sans', sans-serif; @@ -165,9 +169,30 @@ white-space: nowrap; text-overflow: ellipsis; overflow: hidden; - max-width: calc(100% - 39px); + /*max-width: calc(100% - 39px);*/ } + .displayName{ + float: left; + width: 87%; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + /* margin: 2% auto;*/ + /*display: flex;*/ + align-items: center; + padding-left: 3%; + /*height: 14px;*/ + } + .categoryName{ + + width: 100%; + text-overflow: ellipsis; + overflow: hidden; + + + + } .content_sidebar::-webkit-scrollbar { margin-top: 0.5rem; @@ -260,11 +285,15 @@ display: none; } - .sidebarCategory .pinButton { + .sidebarCategory .pinButton, .sidebarItem .pinButton { background-color: transparent; border: 0; position: absolute; - right: 0; + width: 10%; + float: left; + /* padding-top: 4px; */ + /* font-size: 39px; */ + height: 1.5em; } #sb_scrollToTopButton.closed { @@ -276,8 +305,13 @@ background-color: transparent; border: 0; - position: absolute; - right: 0; + /*position: absolute; + right: 0;*/ + + position: relative; + width: 10%; + float: left; + } .previewButton { @@ -294,12 +328,12 @@ .svg_class { width: 24px; - height: 24px; + height: 18px; fill: var(--border-color); cursor: pointer; - /* hacky fix */ + /* hacky fix margin-top: -5px; - margin-left: -25px; + margin-left: -25px;*/ } .pin_normal { diff --git a/app/html/sidebar.html b/app/html/sidebar.html index 65dce86..2aa748c 100644 --- a/app/html/sidebar.html +++ b/app/html/sidebar.html @@ -4,13 +4,25 @@ × C +
-
- +
diff --git a/app/js/functions/settings.js b/app/js/functions/settings.js index b63369b..8f2a8a8 100644 --- a/app/js/functions/settings.js +++ b/app/js/functions/settings.js @@ -156,7 +156,39 @@ async function settingsSetup(app,$el) { }, }); - + addSBSetting( "sb_settingsDiv",{ + id: "auto_hide", + name: "Auto Show Sidebar", + defaultValue: "false", + type: "boolean", + local: true, + + + onChange(value) { + + }, + }); + + await addSBSetting( "sb_settingsDiv", { + id: "tree_view", + name: "Node Category Tree View", + defaultValue: "multi", + type: "dropdown", + info: "NOTE: The new view will be applied after a page refresh!", + options: [ + { value: "mono", label: "MONO" }, + { value: "multi", label: "MULTILEVEL" } + + + ], + onChange(value) { + + defaultSearchOrder = value; + + + }, + }); + addSBSetting( "sb_settingsDiv",{ id: "font", name: "Font Size", @@ -168,7 +200,7 @@ async function settingsSetup(app,$el) { step: 1, }, onChange(value) { - addDynamicCSSRule('.sidebarCategory, #sidebarBookmarks', 'font-size', value + 'px'); + addDynamicCSSRule('.sidebarCategory, .sidebarFolder, #sidebarBookmarks', 'font-size', value + 'px'); }, }); @@ -337,7 +369,7 @@ async function settingsSetup(app,$el) { step: 1, }, onChange(value) { - addDynamicCSSRule('.sidebarCategory, #sidebarBookmarks', 'border-radius', value + 'px'); + addDynamicCSSRule('.sidebarCategory, .sidebarFolder, #sidebarBookmarks', 'border-radius', value + 'px'); addDynamicCSSRule('.sidebarItem', 'border-radius', value + 'px'); }, }); @@ -374,7 +406,7 @@ async function settingsSetup(app,$el) { addDynamicCSSRule('.content_sidebar', 'background', 'rgb(from var(--comfy-menu-bg) r g b / ' + value_perc + '%)'); addDynamicCSSRule('#sidebar_views', 'background', 'rgb(from var(--comfy-menu-bg) r g b / ' + value_perc + '%)'); addDynamicCSSRule('#sidebar_views', 'border-left', '2px rgb(from var(--comfy-menu-bg) r g b / ' + (parseFloat(value) + 10) + '%)'); - addDynamicCSSRule('.sidebarCategory, #sidebarBookmarks', 'background-color', 'rgb(from var(--comfy-input-bg) r g b / ' + value_perc + '%)'); + addDynamicCSSRule('.sidebarCategory, .sidebarFolder, #sidebarBookmarks', 'background-color', 'rgb(from var(--comfy-input-bg) r g b / ' + value_perc + '%)'); }, @@ -416,8 +448,8 @@ async function settingsSetup(app,$el) { addDynamicCSSRule('.dragHandle', 'float', 'right'); addDynamicCSSRule('.sidebar', 'left', 'unset'); addDynamicCSSRule('.sidebar', 'right', '0'); - addDynamicCSSRule('.sidebar-header', 'right', '16px'); - addDynamicCSSRule('#sb_scrollToTopButton', 'right', '12px'); + addDynamicCSSRule('.sidebar-header', 'right', '52px'); + addDynamicCSSRule('#sb_scrollToTopButton', 'right', '48px'); } if (value == "left") { @@ -427,7 +459,7 @@ async function settingsSetup(app,$el) { addDynamicCSSRule('.sidebar', 'left', '0'); addDynamicCSSRule('.sidebar', 'right', 'unset'); addDynamicCSSRule('.sidebar-header', 'right', 'unset'); - addDynamicCSSRule('#sb_scrollToTopButton', 'right', '56px'); + addDynamicCSSRule('#sb_scrollToTopButton', 'right', '26px'); } sbPosition = value; @@ -642,7 +674,7 @@ function resetSettings() { removeConfiguration(setting); }); - const localSettings = ["sidebarWidth","sb_pinned_collapsed","sb_minimized","sb_current_tab","sb_workflow_preview","sb_workflow_replace"]; + const localSettings = ["sidebarWidth","sb_pinned_collapsed","sb_minimized","sb_current_tab","sb_workflow_preview","sb_workflow_replace","sb_migrated"]; localSettings.forEach(setting => { try{ @@ -663,7 +695,7 @@ function factoryReset() { } factoryResetSettings(); - const localSettings = ["sidebarWidth","sb_pinned_collapsed","sb_minimized","sb_current_tab","sb_workflow_preview","sb_workflow_replace"]; + const localSettings = ["sidebarWidth","sb_pinned_collapsed","sb_minimized","sb_current_tab","sb_workflow_preview","sb_workflow_replace","sb_migrated"]; localSettings.forEach(setting => { try{ diff --git a/app/js/functions/utils.js b/app/js/functions/utils.js index 8b81e7b..9a41608 100644 --- a/app/js/functions/utils.js +++ b/app/js/functions/utils.js @@ -213,7 +213,6 @@ function createContextMenu(event, subMenus,settingsData) { try { - if (settingsData.menuctx_opt_callback[index].indexOf(".") !== -1) { // Handle sub menu option @@ -234,9 +233,6 @@ function createContextMenu(event, subMenus,settingsData) { if (typeof (window[settingsData.menuctx_opt_callback[index]]) === 'function') { li.addEventListener('click', () => { // Handle main option click - - - window[settingsData.menuctx_opt_callback[index]](event); }); @@ -244,6 +240,7 @@ function createContextMenu(event, subMenus,settingsData) { } } catch (error) { console.log(error); + } menuOptions.appendChild(li); }); @@ -334,17 +331,41 @@ async function setContextMenu(settingsData,class_menu_item,main=0,exluded_class= subMenu.appendChild(li); try { - if (typeof (window[settingsData.menuctx_sub_opt_callback[i][j]]) === 'function') { - li.addEventListener('click', () => { - // Handle main option click - - //alert(`Selected option: ${settingsData.menuctx_subOptions[i][j]}`); - - - window[settingsData.menuctx_sub_opt_callback[i][j]](lastTargetClicked,settingsData.menuctx_subOptions[i][j]); - - }); + if (settingsData.menuctx_sub_opt_callback[i][j].indexOf(".") !== -1) { + + // Handle sub menu option + var mainfunc = settingsData.menuctx_sub_opt_callback[i][j].split(".")[0]; + var subfunc = settingsData.menuctx_sub_opt_callback[i][j].split(".")[1]; + if (typeof (window[mainfunc][subfunc]) === 'function') { + + + li.addEventListener('click', () => { + // Handle sub menu click + + window[mainfunc][subfunc](lastTargetClicked,settingsData.menuctx_subOptions[i][j]); + + }); + + } + } + else{ + + if (typeof (window[settingsData.menuctx_sub_opt_callback[i][j]]) === 'function') { + li.addEventListener('click', () => { + // Handle main option click + + + window[settingsData.menuctx_sub_opt_callback[i][j]](lastTargetClicked,settingsData.menuctx_subOptions[i][j]); + + }); + } } + + + + + + } catch (error) { console.log(error); } @@ -411,24 +432,24 @@ async function reloadCtxMenu() { } -function setNodeStatus(node,status) { - try{ - const categoryNodeStatus = JSON.parse(localStorage.getItem('sb_categoryNodeStatus')) || {}; +function setNodeStatus(varname,node,status) { + try{//'sb_categoryNodeStatus' + const categoryNodeStatus = JSON.parse(localStorage.getItem(varname)) || {}; categoryNodeStatus[node] = [status]; - localStorage.setItem('sb_categoryNodeStatus', JSON.stringify(categoryNodeStatus)); + localStorage.setItem(varname, JSON.stringify(categoryNodeStatus)); }catch(err){ } } -function getNodeStatus(node) { +function getNodeStatus(varname,node) { try{ - const categoryNodeStatus = JSON.parse(localStorage.getItem('sb_categoryNodeStatus')) || {}; + const categoryNodeStatus = JSON.parse(localStorage.getItem(varname)) || {}; return categoryNodeStatus[node] || "none"; }catch(err){ } return "none"; } function getNodesStatus() { - let categoryNodeStatus = JSON.parse(localStorage.getItem('sb_categoryNodeStatus')) || {}; + let categoryNodeStatus = JSON.parse(localStorage.getItem(varname)) || {}; return categoryNodeStatus; } diff --git a/app/js/sidebar_node.js b/app/js/sidebar_node.js index ab35bdb..3e1c01a 100644 --- a/app/js/sidebar_node.js +++ b/app/js/sidebar_node.js @@ -42,11 +42,6 @@ try { let categorySearchToggle = false; - - - - - function getSidebarItemIds() { const sidebarItems = document.querySelectorAll("#sidebarBookmarks .sidebarItem"); const itemIds = []; @@ -98,8 +93,8 @@ function postPinned() { } let sidebar_width = parseInt(getVar("sidebarWidth")) || 500; previewDiv.style.top = `${previewDivTop}px`; - const previewDivLeft = sidebar_width - sidebad_view_width; - + const previewDivLeft = sidebar_width - sidebad_view_width + correction_offset; + if (sbPosition == "left") { previewDiv.style.left = `${previewDivLeft}px`; } else { @@ -288,8 +283,6 @@ async function createCategoryList() { if (defaultSearchToggle == "original" && itemSearchInput.value != "") { //search in all .sidebarItem handleSearch(categorySearchToggle, "#content_sidebar_home", "searchInput") - - } else { updateList(); @@ -445,98 +438,154 @@ async function createCategoryList() { } + function buildTree(categories, useSubcategories = true) { + const tree = {}; + + for (const category in categories) { + const parts = useSubcategories ? category.split('/').filter(part => part !== '') : [category]; + + let current = tree; + if (parts.length > 0 || !useSubcategories) { + + + for (const part of parts) { + if (!current[part]) { + current[part] = {}; + } + current = current[part]; + } + + if (!current.items) { + current.items = []; + } + + categories[category].forEach(displayName => { + current.items.push(displayName); + }); + } + } + + return tree; + } + + function createHtmlFromTree(tree, parentElement,pinnedItems) { + for (const key in tree) { + if (key !== 'items') { + const subfolderLi = document.createElement('li'); + subfolderLi.textContent = "🗀 " + key; + subfolderLi.classList.add('sidebarCategory'); + subfolderLi.dataset.nameworkflow = key; + + const subfolderUl = document.createElement('ul'); + subfolderUl.style.display = "none"; + subfolderUl.classList.add('subfolder'); + subfolderUl.dataset.subfolder = key; + subfolderUl.id = key + "_ul"; + subfolderUl.classList.add("displayNamesList"); + + subfolderLi.appendChild(subfolderUl); + parentElement.appendChild(subfolderLi); + + createHtmlFromTree(tree[key], subfolderUl,pinnedItems); + } + } + + if (tree.items) { + tree.items.forEach(displayName => { + try { + + const displayNameMain = document.createElement('div'); + displayNameMain.classList.add('displayName'); + displayNameMain.title = displayName.title; + displayNameMain.id = displayName.type; + + displayNameMain.textContent = displayName.title; + + + + const displayNameItem = document.createElement('li'); + displayNameItem.classList.add('sidebarItem'); + //displayNameItem.innerHTML = "
"+displayName.title+"
"; + displayNameItem.title = displayName.title; + displayNameItem.draggable = true; + displayNameItem.appendChild(displayNameMain); + + // JOVIMETRIX CUT-OUT FOR CUSTOM COLORED NODES + if (CUSTOM_COLORS) { + let color = CUSTOM_COLORS[displayName.title]; + const nodeData = displayName?.nodeData; + if (color === undefined) { + if (nodeData !== undefined) { + let k = nodeData.category; + while (k) { + color = CUSTOM_COLORS[k]; + if (color) { + color = color.title; + break; + } + const last = k.lastIndexOf('/'); + k = last !== -1 ? k.substring(0, last) : ''; + } + } + } else { + color = color.title; + } + if (color) { + displayNameItem.style = `background: ${color}`; + } + } + // + + displayNameItem.id = displayName.type; + + /* Create Pin Button */ + + const pinButton = document.createElement('button'); + pinButton.classList.add('pinButton'); + + let add_class = ""; + + if (pinnedItems.includes(displayName.type)) { + add_class = "pinned"; + } + pinButton.innerHTML = ` + + + `; + + displayNameItem.appendChild(pinButton); + /* End Pin Button */ + + parentElement.appendChild(displayNameItem); + } catch (err) { + console.log(err); + } + }); + } + } + + async function renderList() { return new Promise(async (resolve, reject) => { const pinnedItems = await loadPinnedItems(); // const sidebarCustomNodes = document.getElementById("sidebarCustomNodes"); - for (const category in categories) { - const categoryItem = document.createElement("li"); - categoryItem.classList.add("sidebarCategory"); - categoryItem.textContent = category; - - const displayNamesList = document.createElement("ul"); - displayNamesList.style.display = "none"; - categoryItem.appendChild(displayNamesList); - - categories[category].forEach(displayName => { - try { - const displayNameItem = document.createElement("li"); - displayNameItem.classList.add("sidebarItem"); - displayNameItem.textContent = displayName.title; - displayNameItem.title = displayName.title; - displayNameItem.draggable = true; - - // JOVIMETRIX CUT-OUT FOR CUSTOM COLORED NODES - // - if (CUSTOM_COLORS) { - let color = CUSTOM_COLORS[displayName.title]; - const nodeData = displayName?.nodeData; - if (color === undefined) { - if (nodeData !== undefined) { - let k = nodeData.category; - while (k) { - color = CUSTOM_COLORS[k]; - if (color) { - color = color.title; - break; - } - const last = k.lastIndexOf('/'); - k = last !== -1 ? k.substring(0, last) : ''; - } - } - } else { - color = color.title; - } - if (color) { - displayNameItem.style = `background: ${color}`; - } - } - // - - displayNameItem.id = displayName.type; - - /* Create Pin Button */ - const pinButton = document.createElement("button"); - pinButton.classList.add("pinButton"); - - let add_class = ""; - - if (pinnedItems.includes(displayName.type)) { - add_class = "pinned"; - } - pinButton.innerHTML = ` - - - `; - - - displayNameItem.appendChild(pinButton); - /* End Pin Button */ - - - displayNamesList.appendChild(displayNameItem); - } catch (err) { - console.log(err); - } - }); - - categoryItem.addEventListener("click", function (event) { - - if (event.target === event.currentTarget) { - - displayNamesList.style.display = displayNamesList.style.display === "none" ? "block" : "none"; - } - }); - - - - sidebarCustomNodes.appendChild(categoryItem); - - + + let tree_view = await getConfiguration("sb_tree_view") || "multi"; + if (tree_view=="multi" ) { + tree_view = true; + } else{ + tree_view = false; } + const tree = buildTree(categories,tree_view); + + + createHtmlFromTree(tree, sidebarCustomNodes,pinnedItems); + + + if (!categorySearchToggle && itemSearchInput.value != "") { sdExpandAll(1); @@ -585,7 +634,7 @@ async function createCategoryList() { previewDiv.style.top = `${previewDivTop}px`; - const previewDivLeft = sidebar_width - sidebad_view_width; + const previewDivLeft = sidebar_width - sidebad_view_width + correction_offset; if (sbPosition == "left") { previewDiv.style.left = `${previewDivLeft}px`; @@ -615,6 +664,25 @@ async function createCategoryList() { }); + const categoryItems = document.querySelectorAll("#content_sidebar_home .sidebarCategory"); + categoryItems.forEach(function (folderItem) { + folderItem.addEventListener("click", function (event) { + + if (event.target === event.currentTarget) { + const displayNamesList = event.target.querySelector("ul"); + + displayNamesList.style.display = displayNamesList.style.display === "none" ? "block" : "none"; + + setNodeStatus('sb_categoryNodeStatus',displayNamesList.parentElement.dataset.nameworkflow, displayNamesList.style.display) + } + }); + + + + }); + + + reloadCtxMenu() resolve(); }); @@ -665,10 +733,6 @@ async function createCategoryList() { - - - - } @@ -833,16 +897,17 @@ function toggleSHSB(force = undefined) { searchCategoryIcon.classList.add('closed'); search_bar.classList.add('closed'); scrollToTopButton.classList.add('closed'); + } else { side_bar.classList.remove('closed'); clearIcon.classList.remove('closed'); searchCategoryIcon.classList.remove('closed'); search_bar.classList.remove('closed'); scrollToTopButton.classList.remove('closed'); - + setVar("sb_minimized", "true"); } } else { - + if (side_bar.classList.contains('closed')) { side_bar.classList.remove('closed'); clearIcon.classList.remove('closed'); @@ -869,30 +934,31 @@ function toggleSHSB(force = undefined) { }); + + + if (getVar("sb_minimized") == "false") { - if (getVar("sb_minimized") == "false") { - - if (force == undefined) { - setVar("sb_minimized", true); - } - main_sidebar.style.width = '45px'; - } else { - - if (force == undefined) { - setVar("sb_minimized", false); - main_sidebar.style.width = getVar("sidebarWidth") || '300px'; - } else if (force == true) { - - main_sidebar.style.width = '45px'; console.log("minimized") - + if (force == undefined) { + setVar("sb_minimized", true); + } + main_sidebar.style.width = '45px'; } else { - main_sidebar.style.width = getVar("sidebarWidth") || '300px'; + + if (force == undefined) { + setVar("sb_minimized", false); + main_sidebar.style.width = getVar("sidebarWidth") || '300px'; + } else if (force == true) { + + main_sidebar.style.width = '45px'; + + } else { + main_sidebar.style.width = getVar("sidebarWidth") || '300px'; + } + } - } - - + } @@ -969,6 +1035,7 @@ function processViews(viewsData, settingsData) { div_panel_title.id = "panel_title_" + panel; div_panel_title.innerHTML = view.id.replace("_", " "); + @@ -1001,12 +1068,15 @@ function processViews(viewsData, settingsData) { //inherit width from bsb div_button.dataset.tab = view.id; - div_button.alt = view.description; + div_button.title = description; div_button.innerHTML += view.icon; div_button.addEventListener('click', function () { const tabId = "panel_" + this.getAttribute('data-tab'); switchTab(tabId) + if (getVar("sb_auto_hide") == "false" && getVar("sb_minimized") == "true") { + toggleSHSB(false); + } }); sbv.appendChild(div_button); }); @@ -1019,12 +1089,17 @@ function processViews(viewsData, settingsData) { switchTab(getVar('sb_current_tab')); const sidebar_view = document.getElementById('sidebar_views'); const sidebar = document.getElementById('sidebar'); + + + sidebar_view.addEventListener('mouseover', function () { - if (getVar('sb_minimized') == "true") { - toggleSHSB(false); + + if (getVar('sb_auto_hide') == "true") { + if (getVar('sb_minimized') == "true") { + toggleSHSB(false); + } } - }); document.addEventListener('click', function (event) { //if click is outside sidebar id element @@ -1077,7 +1152,9 @@ async function SidebarPostBoot() { //only for home switch_home.addEventListener('click', function () { switchTab("content_sidebar_home", 1) - + if (getVar("sb_auto_hide") == "false" && getVar("sb_minimized") == "true") { + toggleSHSB(false); + } }); diff --git a/app/panels/custom_categories/custom_categories.html b/app/panels/custom_categories/custom_categories.html index 065dcc7..643de0c 100644 --- a/app/panels/custom_categories/custom_categories.html +++ b/app/panels/custom_categories/custom_categories.html @@ -12,9 +12,9 @@ - + diff --git a/app/panels/custom_categories/custom_categories.jsb b/app/panels/custom_categories/custom_categories.jsb index a438c61..763d575 100644 --- a/app/panels/custom_categories/custom_categories.jsb +++ b/app/panels/custom_categories/custom_categories.jsb @@ -33,6 +33,7 @@ const panel_title_custom_categories = document.getElementById("panel_title_custo const button_custom_categories = document.createElement("button"); button_custom_categories.classList = "expand_node"; +button_custom_categories.title = "Expand/Collapse"; button_custom_categories.innerHTML = ` !assignedCategories.includes(category)); @@ -767,11 +760,6 @@ function colorCategoryCallback(e) { - - - - - async function colorCategory(name, value) { @@ -800,7 +788,9 @@ async function createContextualMenu() { new_menu_options_callback = []; new_submenu_options_callback = []; new_menu_options.push("Add to category"); - + /*new_menu_options.push("Pin It"); + new_menu_options_callback.push("dummy"); + new_menu_options_callback.push("test_altert");*/ //for each category category_menu = await readCategories(); callback_menu = []; @@ -825,6 +815,11 @@ function test_altert(e, trge) { assignNodeToCategory(e.target.id, [trge]); } +function dummy(e, trge) { + +} + + const searchCategoryIconCustomCategory = document.querySelector(".searchCategoryIconCustomCategory"); diff --git a/app/panels/custom_categories/style.css b/app/panels/custom_categories/style.css index c4f132d..61c073c 100644 --- a/app/panels/custom_categories/style.css +++ b/app/panels/custom_categories/style.css @@ -8,6 +8,7 @@ #custom_categories_main{ padding-top: 44px; +padding-bottom: 150px; } #custom_categories_header { margin: 10px 20px; diff --git a/app/panels/custom_templates/custom_templates.html b/app/panels/custom_templates/custom_templates.html index 9097916..c5bb615 100644 --- a/app/panels/custom_templates/custom_templates.html +++ b/app/panels/custom_templates/custom_templates.html @@ -3,11 +3,11 @@ × - C + C - - + +
\ No newline at end of file diff --git a/app/panels/custom_templates/custom_templates.jsb b/app/panels/custom_templates/custom_templates.jsb index 2ebfb9e..13175cd 100644 --- a/app/panels/custom_templates/custom_templates.jsb +++ b/app/panels/custom_templates/custom_templates.jsb @@ -112,6 +112,7 @@ var custom_templates = (function () { const button_custom_templates = document.createElement("button"); button_custom_templates.classList = "expand_node"; + button_custom_templates.title = "Expand/Collapse"; button_custom_templates.innerHTML = ` { try { const displayNameItem = document.createElement("li"); @@ -582,7 +584,7 @@ var custom_templates = (function () { previewDiv.style.top = `${previewDivTop}px`; - const previewDivLeft = sidebar_width - sidebad_view_width; + const previewDivLeft = sidebar_width - sidebad_view_width+correction_offset; if (sbPosition == "left") { @@ -631,7 +633,7 @@ var custom_templates = (function () { displayNamesList.style.display = displayNamesList.style.display === "none" ? "block" : "none"; - setNodeStatus(displayNamesList.parentElement.dataset.nametemplate, displayNamesList.style.display) + setNodeStatus('sb_templateNodeStatus',displayNamesList.parentElement.dataset.nametemplate, displayNamesList.style.display) } }); @@ -1166,7 +1168,7 @@ var custom_templates = (function () { async function createContextualMenu() { //const menuOptions = document.getElementById('menu-options'); - new_menu_options_callback = []; + //new_menu_options_callback = []; //for each template template_menu = await readTemplates(); callback_menu = []; diff --git a/app/panels/custom_templates/style.css b/app/panels/custom_templates/style.css index cfd0fde..ccbf22a 100644 --- a/app/panels/custom_templates/style.css +++ b/app/panels/custom_templates/style.css @@ -8,6 +8,7 @@ #custom_templates_main{ padding-top: 0px; +padding-bottom: 150px; } #custom_templates_header { margin: 10px 20px; diff --git a/app/panels/custom_workflows/custom_workflows.html b/app/panels/custom_workflows/custom_workflows.html index dfa4093..e8cdc07 100644 --- a/app/panels/custom_workflows/custom_workflows.html +++ b/app/panels/custom_workflows/custom_workflows.html @@ -3,11 +3,12 @@ × - C + C - - + +
+
jjjjjjjjjjjjjj
\ No newline at end of file diff --git a/app/panels/custom_workflows/custom_workflows.jsb b/app/panels/custom_workflows/custom_workflows.jsb index cc9d7e0..ecb1c25 100644 --- a/app/panels/custom_workflows/custom_workflows.jsb +++ b/app/panels/custom_workflows/custom_workflows.jsb @@ -37,6 +37,30 @@ var custom_workflows = (function () { return '
' + nodeID + '
USED NODES
'; } } + + + async function getWorkflow(nodeID, jsondata) { + try { + const req = JSON.stringify({ action: 'load', path: jsondata }); + + + const response = await fetch('/sidebar/workflow', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: req + }); + + const data = await response.json(); + + return data; + + } catch (e) { + console.log(e); + return NaN; + } + } // MODAL function createCustomHtml(type, name, elem = null) { @@ -129,6 +153,7 @@ var custom_workflows = (function () { const button_custom_workflows = document.createElement("button"); button_custom_workflows.classList = "expand_node"; + button_custom_workflows.title = "Expand/Collapse"; button_custom_workflows.innerHTML = ` response.json()) .then(data => { app.loadGraphData(data); + draggedElement.dataset.data = NaN; }); } else if (draggedElement.id === 'sidebarWorkflowItem' && (event.target.classList.contains('sidebarCategory') || event.target.classList.contains('displayNamesList'))) { const ulElement = event.target.querySelector('ul'); + if (ulElement) { ulElement.appendChild(draggedElement); } else { @@ -345,7 +415,10 @@ var custom_workflows = (function () { updateConfiguration('sb_workflowNodeMap', JSON.stringify(getCustomWorkflowStructure())); renderList("custom_workflows_main"); - } else if (draggedElement.id === 'sidebarWorkflowItem' && event.target.parentElement.parentElement.classList.contains('sidebarCategory')) { + } + + + else if (draggedElement.id === 'sidebarWorkflowItem' && event.target.parentElement.parentElement.classList.contains('sidebarCategory')) { event.target.parentElement.insertBefore(draggedElement, event.target); updateConfiguration('sb_workflowNodeMap', JSON.stringify(getCustomWorkflowStructure())); renderList("custom_workflows_main"); @@ -374,7 +447,7 @@ var custom_workflows = (function () { let node = []; const objMain = document.getElementById(idcat); objMain.querySelectorAll(".sidebarItem").forEach(function (item) { - node.push(item.dataset.nameworkflow) + node.push(item.dataset.id) }); return node @@ -384,7 +457,7 @@ var custom_workflows = (function () { let cat = {}; const custom_workflows_main = document.getElementById("custom_workflows_main"); custom_workflows_main.querySelectorAll(".sidebarCategory").forEach(function (item) { - + cat[item.dataset.nameworkflow] = getCustomNodeOrder(item.dataset.nameworkflow + "_ul") }); @@ -405,7 +478,87 @@ var custom_workflows = (function () { } - + function splitPath(path) { + // Replace all backslashes with forward slashes + let normalizedPath = path.replace(/\\/g, '/'); + // Split the normalized path by the forward slash + let pathArray = normalizedPath.split('/'); + // Remove any empty strings from the array (which can happen if there are trailing slashes) + pathArray = pathArray.filter(part => part !== ''); + return pathArray; + } + + function buildTree(workflows) { + const tree = {}; + + for (const workflow in workflows) { + const subfolder = workflows[workflow].subfolder.replace(/\\/g, '/'); // Normalize path + const parts = subfolder.split('/').filter(part => part !== ''); + + let current = tree; + for (const part of parts) { + if (!current[part]) { + current[part] = {}; + } + current = current[part]; + } + + if (!current.workflows) { + current.workflows = []; + } + + current.workflows.push({ + name: workflows[workflow].name, + path: workflows[workflow].path, + id: workflow + }); + } + + return tree; + } + + + + function createHtmlFromTree(tree, parentElement) { + // Prima, crea le sottocartelle + for (const key in tree) { + if (key !== 'workflows') { + const subfolderLi = document.createElement('li'); + subfolderLi.textContent = "🗀 "+ key; + subfolderLi.classList.add('sidebarFolder'); + subfolderLi.dataset.nameworkflow = key; + + const subfolderUl = document.createElement('ul'); + subfolderUl.classList.add('subfolder'); + subfolderUl.dataset.subfolder = key; + subfolderUl.id = key + "_ul"; + subfolderUl.classList.add("displayNamesList"); + + subfolderLi.appendChild(subfolderUl); + parentElement.appendChild(subfolderLi); + + createHtmlFromTree(tree[key], subfolderUl); + } + } + + // Poi, crea i workflow + if (tree.workflows) { + tree.workflows.forEach(workflow => { + const workflowItem = document.createElement('li'); + workflowItem.classList.add('sidebarItem'); + workflowItem.id = "sidebarWorkflowItem"; + workflowItem.dataset.nameworkflow = workflow.name; + workflowItem.dataset.data = workflow.path; + workflowItem.dataset.id = workflow.id; + workflowItem.textContent = workflow.name; + workflowItem.draggable = true; + parentElement.appendChild(workflowItem); + }); + } + } + + + async function renderList(elementID) { const data = await readComfyWorkflows() //load all workflows @@ -420,13 +573,16 @@ var custom_workflows = (function () { sidebarCustomNodes.innerHTML = ""; exclusion_list = [] + general_index = 0 + + for (const workflow in workflows) { //data[objKey].title.toLowerCase(); let workflowName = workflows[workflow] const workflowItem = document.createElement("li"); workflowItem.classList.add("sidebarCategory"); workflowItem.dataset.nameworkflow = workflowName; - workflowItem.textContent = workflowName; + workflowItem.textContent = "⌬ " + workflowName; workflowItem.draggable = true; const currentColor = await getValueFromConfig("sb_ColorCustomWorkflows", workflowName); @@ -445,7 +601,7 @@ var custom_workflows = (function () { const displayNamesList = document.createElement("ul"); displayNamesList.id = workflowName + "_ul"; displayNamesList.classList.add("displayNamesList"); - displayNamesList.style.display = getNodeStatus(workflowName); + displayNamesList.style.display = getNodeStatus('sb_workflowNodeStatus',workflowName); workflowItem.appendChild(displayNamesList); let displayName = workflow; @@ -460,15 +616,12 @@ var custom_workflows = (function () { const displayNameItem = document.createElement("li"); displayNameItem.classList.add("sidebarItem"); displayNameItem.id = "sidebarWorkflowItem" - displayNameItem.textContent = displayName; - displayNameItem.dataset.nameworkflow = displayName; - displayNameItem.dataset.data = data[displayName]; + displayNameItem.textContent = data[displayName].name; + displayNameItem.dataset.nameworkflow = data[displayName].name; + displayNameItem.dataset.data = data[displayName].path; + displayNameItem.dataset.id = displayName; displayNameItem.draggable = true; - - - - exclusion_list.push(displayName); @@ -483,7 +636,6 @@ var custom_workflows = (function () { if (dragItem.parentElement == event.target.parentElement) { displayNamesList.insertBefore(dragItem, event.target); - reorderNodeInWorkflow(workflowItem.dataset.nameworkflow, getCustomNodeOrder(dragItem.parentElement.id)) } @@ -503,6 +655,7 @@ var custom_workflows = (function () { } catch (err) { console.log(err); } + general_index++; } //adding workflows not categorized @@ -510,43 +663,24 @@ var custom_workflows = (function () { sidebarCustomNodes.appendChild(space); let comfy_workflows = await readComfyWorkflows(); - for (const workflow in comfy_workflows) { - - //data[objKey].title.toLowerCase(); - //if workflowname is not in exclusion list - if (!exclusion_list.includes(workflow)) { + comfy_workflows = Object.fromEntries(Object.entries(comfy_workflows).filter(([key]) => !exclusion_list.includes(key))); + + const tree = buildTree(comfy_workflows); + general_index = general_index + (Object.keys(tree).length); - const workflowItem = document.createElement("li"); - workflowItem.classList.add("sidebarItem"); - workflowItem.id = "sidebarWorkflowItem"; - workflowItem.dataset.nameworkflow = workflow; - workflowItem.dataset.data = comfy_workflows[workflow]; - workflowItem.textContent = workflow; - workflowItem.draggable = true; - - - - - - let displayName = workflow; - - - - try { - sidebarCustomNodes.appendChild(workflowItem); - } catch (err) { - console.log(err); - } - } - - - + createHtmlFromTree(tree, sidebarCustomNodes); + if (general_index == 0) { + const noWorkflow = document.createElement("div"); + noWorkflow.classList.add("TIPSidebarItem"); + noWorkflow.textContent = "It's pretty empty here... Try adding your Workflow folder in the sidebar settings! :D"; + sidebarCustomNodes.appendChild(noWorkflow); } + // Preview const sidebarItems_cat = document.querySelectorAll('#' + elementID + ' .sidebarItem'); @@ -587,7 +721,7 @@ var custom_workflows = (function () { previewDiv.style.top = `${previewDivTop}px`; - const previewDivLeft = sidebar_width - sidebad_view_width; + const previewDivLeft = sidebar_width - sidebad_view_width+correction_offset; if (sbPosition == "left") { @@ -639,7 +773,25 @@ var custom_workflows = (function () { displayNamesList.style.display = displayNamesList.style.display === "none" ? "block" : "none"; - setNodeStatus(displayNamesList.parentElement.dataset.nameworkflow, displayNamesList.style.display) + setNodeStatus('sb_workflowNodeStatus',displayNamesList.parentElement.dataset.nameworkflow, displayNamesList.style.display) + } + }); + + + + }); + + + const folderItems = document.querySelectorAll("#custom_workflows_main .sidebarFolder"); + folderItems.forEach(function (folderItem) { + folderItem.addEventListener("click", function (event) { + + if (event.target === event.currentTarget) { + const displayNamesList = event.target.querySelector("ul"); + + displayNamesList.style.display = displayNamesList.style.display === "none" ? "block" : "none"; + + setNodeStatus('sb_workflowNodeStatus',displayNamesList.parentElement.dataset.nameworkflow, displayNamesList.style.display) } }); @@ -790,6 +942,9 @@ var custom_workflows = (function () { alert("Workflow not found!"); } } + + + async function readWorkflows() { let workflowNodeMap = await getNodeMap(); if (workflowNodeMap) { @@ -800,6 +955,8 @@ var custom_workflows = (function () { } } + + // Function to delete a workflow async function removeCategory(e) { @@ -825,38 +982,47 @@ var custom_workflows = (function () { } async function renameWorkflow(path,oldWorkflowName, newWorkflowName) { - - if (containsSpecialCharacters(newWorkflowName, level=1)) { - // Alert the user and return if special characters are found - alert("Workflow name cannot contain special characters."); - return; - } - await fetch('./sidebar/workflow', { - method: 'POST', - body: JSON.stringify({ - action: 'rename', - path: path, - newName: newWorkflowName - }), - headers: { - 'Content-Type': 'application/json' + + + + if (containsSpecialCharacters(newWorkflowName, level=1)) { + // Alert the user and return if special characters are found + alert("Workflow name cannot contain special characters."); + return; } - }).then(response => response.json()) - .then(data => { + + const parts = path.split('/'); + parts.pop(); + + let oldWorkflowNameHash = btoa(parts.join('\\')+'\\'+oldWorkflowName + '.json'); + let newWorkflowNameHash = btoa(parts.join('\\')+'\\'+newWorkflowName + '.json'); + + await fetch('./sidebar/workflow', { + method: 'POST', + body: JSON.stringify({ + action: 'rename', + path: path, + newName: newWorkflowName + }), + headers: { + 'Content-Type': 'application/json' + } + }).then(response => response.json()) + .then(data => { if (data.result == 'OK') { - - renameWorkflowNode("sb_workflowNodeMap", oldWorkflowName, newWorkflowName) - - const element = document.getElementById('panel_workflows'); - let position = element.scrollTop; - closeModal() - renderList("custom_workflows_main"); - //little hack - setTimeout(() => { - element.scrollTop = position; - },200); + renameWorkflowNode("sb_workflowNodeMap", oldWorkflowNameHash, newWorkflowNameHash) + + const element = document.getElementById('panel_workflows'); + let position = element.scrollTop; + closeModal() + renderList("custom_workflows_main"); + + //little hack + setTimeout(() => { + element.scrollTop = position; + },200); } else { alert(data.result); } @@ -1014,7 +1180,14 @@ var custom_workflows = (function () { workflowNodeMap[workflowName].push(nodeName); }); updateConfiguration('sb_workflowNodeMap', JSON.stringify(workflowNodeMap)); + const element = document.getElementById('panel_workflows'); + let position = element.scrollTop; renderList("custom_workflows_main"); + + //little hack + setTimeout(() => { + element.scrollTop = position; + },200); } @@ -1082,11 +1255,12 @@ var custom_workflows = (function () { // Callbacks function removeNodeFromWorkflowCallback(e, trge) { - removeNodeFromWorkflow(e.target.dataset.nameworkflow, e.target.parentElement.parentElement.dataset.nameworkflow) + removeNodeFromWorkflow(e.target.dataset.id, e.target.parentElement.parentElement.dataset.nameworkflow) } function assignNodeToWorkflowCallback(e, trge) { - assignNodeToWorkflow(e.target.id, [trge]); + assignNodeToWorkflow(e.target.dataset.id, [trge]); + } function renameWorkflowCallback(e, trge) { @@ -1105,9 +1279,8 @@ var custom_workflows = (function () { function removeWorkflowCallback(e, trge) { - const name = e.target.dataset.nameworkflow; + const name = e.target.textContent; const path = e.target.dataset.data; - removeWorkflow(name,path) } function colorWorkflowCallback(e) { @@ -1117,7 +1290,14 @@ var custom_workflows = (function () { //colorWorkflow(e.target.id, e.target.parentElement.parentElement.dataset.nameworkflow) } + async function openWorkflowInNewTabCallback(e, trge) { + var win = window.open(`${window.location.origin}/?wfsb=${e.target.dataset.id}`, '_blank'); + win.focus(); + + } + + async function colorWorkflow(name, value) { @@ -1142,15 +1322,15 @@ var custom_workflows = (function () { async function createContextualMenu() { //const menuOptions = document.getElementById('menu-options'); - new_menu_options_callback = []; + //new_menu_options_callback = []; //for each workflow - workflow_menu = await readWorkflows(); - callback_menu = []; - for (let i = 0; i < workflow_menu.length; i++) { - callback_menu.push("assignNodeToWorkflowCallback"); + //workflow_menu = await readWorkflows(); + //callback_menu = []; + //for (let i = 0; i < workflow_menu.length; i++) { + // callback_menu.push("assignNodeToWorkflowCallback"); - } + //} console.log("custom workflow panel loaded") @@ -1175,6 +1355,81 @@ var custom_workflows = (function () { } }); + + function checkAndLoadWorkflow() { + const url = new URL(window.location.href); + if (url.searchParams.has('wfsb')) { + + const wfsbValue = url.searchParams.get('wfsb'); + + const req = JSON.stringify({ action: 'load', path: atob(wfsbValue) }); + fetch('/sidebar/workflow', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: req + }) + .then(response => response.json()) + .then(data => { + app.loadGraphData(data); + }); + url.searchParams.delete('wfsb'); + window.history.replaceState({}, document.title, url.pathname + url.search); + } + } + + + /*temp funciton*/ + async function migrationSettings() { + if (!getVar("sb_migrated")) { + fetch('/sidebar/backup', { + method: 'GET', + headers: { + 'Content-Type': 'application/json' + }, + }) + .then(response => response.json()) + .then(async data => { + if (data.result === "OK") { + console.log("Starting migration..."); + // Get actual config + let jsonObject = await getNodeMap(); + // Get actual list of workflows + let workflowNodeMap = await readComfyWorkflows(); + + function findKeyByName(jsonObject, targetName) { + for (let key in jsonObject) { + if (jsonObject[key].name === targetName) { + return key; + } + } + return null; + } + + // Iterate over the workflow + for (let workflow in jsonObject) { + let nodes = jsonObject[workflow]; + for (let node of nodes) { + let key = findKeyByName(workflowNodeMap, node); + if (key !== null) { + await renameWorkflowNode("sb_workflowNodeMap", node, key); + } + } + } + setVar("sb_migrated", true); + console.log("Migration completed."); + } else { + console.error("Backup failed, migration aborted."); + } + }) + .catch(error => { + console.error("Error during backup request:", error); + }); + } +} + + return { openModal, addSidebarStyles, @@ -1195,14 +1450,19 @@ var custom_workflows = (function () { removeNodeFromWorkflowCallback, removeWorkflowCallback, assignNodeToWorkflowCallback, + openWorkflowInNewTabCallback, renameWorkflowCallback, renameCategoryCallback, colorWorkflowCallback, - resetColorWorkflow + resetColorWorkflow, + migrationSettings, + checkAndLoadWorkflow }; })(); +custom_workflows.migrationSettings(); custom_workflows.addSidebarStyles("panels/custom_workflows/style.css"); custom_workflows.createContextualMenu() -custom_workflows.renderList("custom_workflows_main"); \ No newline at end of file +custom_workflows.renderList("custom_workflows_main"); +custom_workflows.checkAndLoadWorkflow(); diff --git a/app/panels/custom_workflows/style.css b/app/panels/custom_workflows/style.css index ddfae5b..d72799e 100644 --- a/app/panels/custom_workflows/style.css +++ b/app/panels/custom_workflows/style.css @@ -8,6 +8,7 @@ #custom_workflows_main{ padding-top: 44px; +padding-bottom: 150px; } #custom_workflows_header { margin: 10px 20px; @@ -42,6 +43,23 @@ cursor: pointer; border: 2px solid var(--descrip-text); } + + +.sidebarFolder { + list-style-type: none; + font-family: 'Open Sans', sans-serif; + text-transform: capitalize; + margin: 2px; + background-color: var(--comfy-input-bg); + opacity: 1; + border-radius: 8px; + padding-top: 11px; + font-size: 15px; + padding-bottom: 8px; +} + + + .sb-workflow-dialog { outline: 0; border: 0; @@ -136,4 +154,15 @@ border: 1px solid var(--border-color); } #refreshlButton{ margin-left: 48px !important; +} + + +.TIPSidebarItem { + color: var(--border-color); + + font-family: 'Open Sans'; + font-size: 2rem; + font-weight: bold; + text-align: center; + } \ No newline at end of file diff --git a/app/views/views.json b/app/views/views.json index 4ebf518..ea0ab0e 100644 --- a/app/views/views.json +++ b/app/views/views.json @@ -1,7 +1,7 @@ [ { "id": "custom_categories", - "icon": "⧉", + "icon": "⌬", "description": "Custom Node Categories", "panels": [ "custom_categories" @@ -12,7 +12,7 @@ { "id": "templates", "icon": "Τ", - "description": "Custom Node templates", + "description": "Templates list", "panels": [ "custom_templates" ] @@ -21,7 +21,7 @@ { "id": "workflows", "icon": "W", - "description": "Custom Node workflows", + "description": "Workflows list", "panels": [ "custom_workflows" ] diff --git a/pyproject.toml b/pyproject.toml index ac4e334..c10f44d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "comfyui-n-sidebar" description = "A simple sidebar for ComfyUI." -version = "1.1.3" +version = "1.2.0" license = "LICENSE" [project.urls]