- 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
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
# Config file
|
||||
app/settings.json
|
||||
app/settings.json.bak
|
||||
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
+71
-14
@@ -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)
|
||||
|
||||
+45
-11
@@ -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 {
|
||||
|
||||
+14
-13
@@ -4,13 +4,25 @@
|
||||
<span class="clearIcon">×</span>
|
||||
<span class="searchCategoryIcon">C</span>
|
||||
</div>
|
||||
<div id="sidebar_views">
|
||||
<div class="view_button" id="switch_sidebar" title="Toggle Sidebar">☰</div>
|
||||
<div class="view_button" id="switch_home" data-content="home" title="Home"><svg xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" x="0px" y="0px" viewBox="0 0 100 100"
|
||||
style="enable-background:new 0 0 100 100; fill:var(--input-text);" xml:space="preserve">
|
||||
<g>
|
||||
<path
|
||||
d="M29,82h13V63.1c0-1.7,1.3-3,3-3h10.3c1.7,0,3,1.3,3,3V82H71c1.7,0,3-1.3,3-3V43.2c0-0.8-0.3-1.6-0.9-2.1l-21-21 c-1.2-1.2-3.1-1.2-4.2,0l-21,21c-0.6,0.6-0.9,1.3-0.9,2.1V79C26,80.7,27.3,82,29,82z" />
|
||||
</g>
|
||||
</svg></div>
|
||||
<div class="view_button" id="switch_settings" title="Settings">⚙</div>
|
||||
</div>
|
||||
<div class="content_sidebar" id="content_sidebar_home" data-expanded="false" data-content="panel_content_home">
|
||||
|
||||
|
||||
<div id="spacer"></div>
|
||||
<label class="sb_label" id="sb_label_pinned">PINNED</label>
|
||||
<ul id="sidebarBookmarks"></ul>
|
||||
<label class="sb_label" id="sb_label_custom">CUSTOM NODES<button class="expand_node">
|
||||
<label class="sb_label" id="sb_label_custom">CUSTOM NODES<button class="expand_node" title="Expand/Collapse">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 52 52" enable-background="new 0 0 52 52"
|
||||
xml:space="preserve">
|
||||
<path
|
||||
@@ -26,18 +38,7 @@
|
||||
<ul id="sidebarCustomNodes"></ul>
|
||||
</div>
|
||||
|
||||
<div id="sidebar_views">
|
||||
<div class="view_button" id="switch_sidebar">☰</div>
|
||||
<div class="view_button" id="switch_home" data-content="home"><svg xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" x="0px" y="0px" viewBox="0 0 100 100"
|
||||
style="enable-background:new 0 0 100 100; fill:var(--input-text);" xml:space="preserve">
|
||||
<g>
|
||||
<path
|
||||
d="M29,82h13V63.1c0-1.7,1.3-3,3-3h10.3c1.7,0,3,1.3,3,3V82H71c1.7,0,3-1.3,3-3V43.2c0-0.8-0.3-1.6-0.9-2.1l-21-21 c-1.2-1.2-3.1-1.2-4.2,0l-21,21c-0.6,0.6-0.9,1.3-0.9,2.1V79C26,80.7,27.3,82,29,82z" />
|
||||
</g>
|
||||
</svg></div>
|
||||
<div class="view_button" id="switch_settings">⚙</div>
|
||||
</div>
|
||||
|
||||
<div class="dragHandle" id="dragHandle"></div>
|
||||
<div id="previewDiv"></div>
|
||||
<div id="sb-modal-backdrop-settings" class="sb-modal-backdrop-settings sb_hidden"></div>
|
||||
|
||||
@@ -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{
|
||||
|
||||
+42
-21
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+201
-124
@@ -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 = "<div class='displayName'>"+displayName.title+"</div>";
|
||||
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 = `<svg class="svg_class" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path class="pin_normal ${add_class}" d="M19.1835 7.80516L16.2188 4.83755C14.1921 2.8089 13.1788 1.79457 12.0904 2.03468C11.0021 2.2748 10.5086 3.62155 9.5217 6.31506L8.85373 8.1381C8.59063 8.85617 8.45908 9.2152 8.22239 9.49292C8.11619 9.61754 7.99536 9.72887 7.86251 9.82451C7.56644 10.0377 7.19811 10.1392 6.46145 10.3423C4.80107 10.8 3.97088 11.0289 3.65804 11.5721C3.5228 11.8069 3.45242 12.0735 3.45413 12.3446C3.45809 12.9715 4.06698 13.581 5.28476 14.8L6.69935 16.2163L2.22345 20.6964C1.92552 20.9946 1.92552 21.4782 2.22345 21.7764C2.52138 22.0746 3.00443 22.0746 3.30236 21.7764L7.77841 17.2961L9.24441 18.7635C10.4699 19.9902 11.0827 20.6036 11.7134 20.6045C11.9792 20.6049 12.2404 20.5358 12.4713 20.4041C13.0192 20.0914 13.2493 19.2551 13.7095 17.5825C13.9119 16.8472 14.013 16.4795 14.2254 16.1835C14.3184 16.054 14.4262 15.9358 14.5468 15.8314C14.8221 15.593 15.1788 15.459 15.8922 15.191L17.7362 14.4981C20.4 13.4973 21.7319 12.9969 21.9667 11.9115C22.2014 10.826 21.1954 9.81905 19.1835 7.80516Z" />
|
||||
<rect class="pin_node" style="opacity:0" x="0" y="0" width="24" height="24" />
|
||||
</svg>`;
|
||||
|
||||
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 = `<svg class="svg_class" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path class="pin_normal ${add_class}" d="M19.1835 7.80516L16.2188 4.83755C14.1921 2.8089 13.1788 1.79457 12.0904 2.03468C11.0021 2.2748 10.5086 3.62155 9.5217 6.31506L8.85373 8.1381C8.59063 8.85617 8.45908 9.2152 8.22239 9.49292C8.11619 9.61754 7.99536 9.72887 7.86251 9.82451C7.56644 10.0377 7.19811 10.1392 6.46145 10.3423C4.80107 10.8 3.97088 11.0289 3.65804 11.5721C3.5228 11.8069 3.45242 12.0735 3.45413 12.3446C3.45809 12.9715 4.06698 13.581 5.28476 14.8L6.69935 16.2163L2.22345 20.6964C1.92552 20.9946 1.92552 21.4782 2.22345 21.7764C2.52138 22.0746 3.00443 22.0746 3.30236 21.7764L7.77841 17.2961L9.24441 18.7635C10.4699 19.9902 11.0827 20.6036 11.7134 20.6045C11.9792 20.6049 12.2404 20.5358 12.4713 20.4041C13.0192 20.0914 13.2493 19.2551 13.7095 17.5825C13.9119 16.8472 14.013 16.4795 14.2254 16.1835C14.3184 16.054 14.4262 15.9358 14.5468 15.8314C14.8221 15.593 15.1788 15.459 15.8922 15.191L17.7362 14.4981C20.4 13.4973 21.7319 12.9969 21.9667 11.9115C22.2014 10.826 21.1954 9.81905 19.1835 7.80516Z" />
|
||||
<rect class="pin_node" style="opacity:0" x="0" y="0" width="24" height="24" />
|
||||
</svg>`;
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
|
||||
@@ -12,9 +12,9 @@
|
||||
<div class="h-header" id="h_search" data-content="panel_head_home">
|
||||
<input type="text" id="searchInputCn" oninput="searchT(this)" placeholder="Search..." autocomplete="off" class="">
|
||||
<span class="clearIconCustomCategory">×</span>
|
||||
<span class="searchCategoryIconCustomCategory">C</span>
|
||||
<span class="searchCategoryIconCustomCategory" title="Category Only">C</span>
|
||||
</div>
|
||||
<button id="openModalButton" onclick="openModal()">+</button>
|
||||
<button id="openModalButton" onclick="openModal()" title="Add New Category">+</button>
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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 = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 52 52" enable-background="new 0 0 52 52" xml:space="preserve">
|
||||
<path d="M48,9.5C48,8.7,47.3,8,46.5,8h-41C4.7,8,4,8.7,4,9.5v3C4,13.3,4.7,14,5.5,14h41c0.8,0,1.5-0.7,1.5-1.5V9.5z"></path>
|
||||
<path d="M48,39.5c0-0.8-0.7-1.5-1.5-1.5h-41C4.7,38,4,38.7,4,39.5v3C4,43.3,4.7,44,5.5,44h41c0.8,0,1.5-0.7,1.5-1.5
|
||||
@@ -120,6 +121,8 @@ function afterRender() {
|
||||
]
|
||||
]
|
||||
}`
|
||||
|
||||
|
||||
customMenuJSON = JSON.parse(customMenu);
|
||||
customMenuItemJSON = JSON.parse(customMenuItem);
|
||||
|
||||
@@ -221,7 +224,7 @@ async function renderList(elementID) {
|
||||
const categoryItem = document.createElement("li");
|
||||
categoryItem.classList.add("sidebarCategory");
|
||||
categoryItem.dataset.namecategory = categoryName;
|
||||
categoryItem.textContent = categoryName;
|
||||
categoryItem.textContent = "⌬ " + categoryName;
|
||||
categoryItem.draggable = true;
|
||||
|
||||
const currentColor = await getValueFromConfig("sb_ColorCustomCategories", categoryName);
|
||||
@@ -238,7 +241,7 @@ async function renderList(elementID) {
|
||||
|
||||
const displayNamesList = document.createElement("ul");
|
||||
displayNamesList.id = categoryName + "_ul";
|
||||
displayNamesList.style.display = getNodeStatus(categoryName);
|
||||
displayNamesList.style.display = getNodeStatus('sb_categoryNodeStatus',categoryName);
|
||||
categoryItem.appendChild(displayNamesList);
|
||||
|
||||
let displayName = category;
|
||||
@@ -365,7 +368,7 @@ async function renderList(elementID) {
|
||||
|
||||
previewDiv.style.top = `${previewDivTop}px`;
|
||||
|
||||
const previewDivLeft = sidebar_width - sidebad_view_width;
|
||||
const previewDivLeft = sidebar_width - sidebad_view_width + correction_offset;
|
||||
|
||||
if (sbPosition == "left") {
|
||||
|
||||
@@ -416,7 +419,7 @@ async function renderList(elementID) {
|
||||
|
||||
displayNamesList.style.display = displayNamesList.style.display === "none" ? "block" : "none";
|
||||
|
||||
setNodeStatus(displayNamesList.parentElement.dataset.namecategory, displayNamesList.style.display)
|
||||
setNodeStatus('sb_categoryNodeStatus',displayNamesList.parentElement.dataset.namecategory, displayNamesList.style.display)
|
||||
}
|
||||
});
|
||||
|
||||
@@ -466,7 +469,6 @@ function getNodesStatus() {
|
||||
|
||||
async function addCategory() {
|
||||
|
||||
|
||||
const newCategoryName = document.getElementById('sb-categoryName').value;
|
||||
// Check if the category name contains special characters
|
||||
if (containsSpecialCharacters(newCategoryName)) {
|
||||
@@ -475,12 +477,8 @@ async function addCategory() {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
let categoryNodeMap = await getNodeMap();
|
||||
|
||||
|
||||
if (!categoryNodeMap[newCategoryName]) {
|
||||
|
||||
categoryNodeMap[newCategoryName] = [];
|
||||
@@ -638,10 +636,6 @@ async function removeKeyConfig(configName) {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////
|
||||
|
||||
async function getCategoriesForNode(nodeName) {
|
||||
@@ -667,7 +661,6 @@ async function getNodesForCategory(categoryName) {
|
||||
|
||||
async function assignNodeToCategory(nodeName, categoryNames) {
|
||||
let categoryNodeMap = await getNodeMap();
|
||||
|
||||
let assignedCategories = await getCategoriesForNode(nodeName);
|
||||
categoryNames = categoryNames.filter(category => !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");
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#custom_categories_main{
|
||||
|
||||
padding-top: 44px;
|
||||
padding-bottom: 150px;
|
||||
}
|
||||
#custom_categories_header {
|
||||
margin: 10px 20px;
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
<input type="text" id="searchTemplateInput" oninput="custom_templates.searchT(this)" placeholder="Search..."
|
||||
autocomplete="off" class="">
|
||||
<span class="clearIconCustomTemplate">×</span>
|
||||
<span class="searchTemplateIconCustomTemplate">C</span>
|
||||
<span class="searchTemplateIconCustomTemplate" title="Category Only">C</span>
|
||||
|
||||
</div>
|
||||
<button id="openModalButton" onclick="custom_templates.openModal()">+</button>
|
||||
<button id="refreshlButton" onclick="custom_templates.renderList('custom_templates_main')">⭮</button>
|
||||
<button id="openModalButton" onclick="custom_templates.openModal()" title="Add New Template">+</button>
|
||||
<button id="refreshlButton" onclick="custom_templates.renderList('custom_templates_main')" title="Refresh">⭮</button>
|
||||
</div>
|
||||
<div id="custom_templates_main">
|
||||
</div>
|
||||
@@ -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 = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 52 52" enable-background="new 0 0 52 52" xml:space="preserve">
|
||||
<path d="M48,9.5C48,8.7,47.3,8,46.5,8h-41C4.7,8,4,8.7,4,9.5v3C4,13.3,4.7,14,5.5,14h41c0.8,0,1.5-0.7,1.5-1.5V9.5z"></path>
|
||||
<path d="M48,39.5c0-0.8-0.7-1.5-1.5-1.5h-41C4.7,38,4,38.7,4,39.5v3C4,43.3,4.7,44,5.5,44h41c0.8,0,1.5-0.7,1.5-1.5
|
||||
@@ -315,6 +316,7 @@ var custom_templates = (function () {
|
||||
function handleDocumentTemplateDropEvent(event) {
|
||||
event.preventDefault();
|
||||
if (draggedElement.id === 'sidebarItem' && event.target.id === 'graph-canvas') {
|
||||
if (draggedElement.dataset.data === 'NaN') { return; }
|
||||
clipboardAction(async () => {
|
||||
const data = JSON.parse(draggedElement.dataset.data);
|
||||
localStorage.setItem("litegrapheditor_clipboard", draggedElement.dataset.data);
|
||||
@@ -323,6 +325,8 @@ var custom_templates = (function () {
|
||||
app.canvas.graph_mouse[0] = coord[0];
|
||||
app.canvas.graph_mouse[1] = coord[1];
|
||||
app.canvas.pasteFromClipboard();
|
||||
localStorage.removeItem("litegrapheditor_clipboard");
|
||||
draggedElement.dataset.data = NaN;
|
||||
});
|
||||
} else if (draggedElement.id === 'sidebarItem' && (event.target.classList.contains('sidebarCategory') || event.target.classList.contains('displayNamesList'))) {
|
||||
const ulElement = event.target.querySelector('ul');
|
||||
@@ -381,7 +385,7 @@ var custom_templates = (function () {
|
||||
}
|
||||
|
||||
async function readComfyTemplates() {
|
||||
let comfy_templates;
|
||||
let comfy_templates = {};
|
||||
try {
|
||||
const response = await fetch('./userdata/comfy.templates.json');
|
||||
comfy_templates = await response.json();
|
||||
@@ -425,7 +429,7 @@ var custom_templates = (function () {
|
||||
const templateItem = document.createElement("li");
|
||||
templateItem.classList.add("sidebarCategory");
|
||||
templateItem.dataset.nametemplate = templateName;
|
||||
templateItem.textContent = templateName;
|
||||
templateItem.textContent = "⌬ " + templateName;
|
||||
templateItem.draggable = true;
|
||||
|
||||
const currentColor = await getValueFromConfig("sb_ColorCustomTemplates", templateName);
|
||||
@@ -443,16 +447,14 @@ var custom_templates = (function () {
|
||||
const displayNamesList = document.createElement("ul");
|
||||
displayNamesList.id = templateName + "_ul";
|
||||
displayNamesList.classList.add("displayNamesList");
|
||||
displayNamesList.style.display = getNodeStatus(templateName);
|
||||
displayNamesList.style.display = getNodeStatus('sb_templateNodeStatus',templateName);
|
||||
templateItem.appendChild(displayNamesList);
|
||||
|
||||
let displayName = template;
|
||||
|
||||
|
||||
const listNodeForTemplate = await getNodesForTemplate(templateName);
|
||||
|
||||
|
||||
|
||||
|
||||
listNodeForTemplate.forEach(displayName => {
|
||||
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 = [];
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#custom_templates_main{
|
||||
|
||||
padding-top: 0px;
|
||||
padding-bottom: 150px;
|
||||
}
|
||||
#custom_templates_header {
|
||||
margin: 10px 20px;
|
||||
|
||||
@@ -3,11 +3,12 @@
|
||||
<input type="text" id="searchWorkflowInput" oninput="custom_workflows.searchT(this)" placeholder="Search..."
|
||||
autocomplete="off" class="">
|
||||
<span class="clearIconCustomWorkflow">×</span>
|
||||
<span class="searchWorkflowIconCustomWorkflow">C</span>
|
||||
<span class="searchWorkflowIconCustomWorkflow" title="Category Only">C</span>
|
||||
|
||||
</div>
|
||||
<button id="openModalButton" onclick="custom_workflows.openModal()">+</button>
|
||||
<button id="refreshlButton" onclick="custom_workflows.renderList('custom_workflows_main')">⭮</button>
|
||||
<button id="openModalButton" onclick="custom_workflows.openModal()" title="Add New Workflow">+</button>
|
||||
<button id="refreshlButton" onclick="custom_workflows.renderList('custom_workflows_main')" title="Refresh">⭮</button>
|
||||
</div>
|
||||
<div id="custom_workflows_main">
|
||||
<div>jjjjjjjjjjjjjj</div>
|
||||
</div>
|
||||
@@ -37,6 +37,30 @@ var custom_workflows = (function () {
|
||||
return '<div class="sb_table"><div class="node_header">' + nodeID + '</div><div class="sb_preview_badge">USED NODES</div><ul><li class="sidebarItem">ERROR</li></ul></div>';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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 = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 52 52" enable-background="new 0 0 52 52" xml:space="preserve">
|
||||
<path d="M48,9.5C48,8.7,47.3,8,46.5,8h-41C4.7,8,4,8.7,4,9.5v3C4,13.3,4.7,14,5.5,14h41c0.8,0,1.5-0.7,1.5-1.5V9.5z"></path>
|
||||
<path d="M48,39.5c0-0.8-0.7-1.5-1.5-1.5h-41C4.7,38,4,38.7,4,39.5v3C4,43.3,4.7,44,5.5,44h41c0.8,0,1.5-0.7,1.5-1.5
|
||||
@@ -165,7 +190,18 @@ var custom_workflows = (function () {
|
||||
|
||||
|
||||
|
||||
function afterRender() {
|
||||
async function afterRender() {
|
||||
const workflows = await readWorkflows();
|
||||
const workflowsString = JSON.stringify(workflows, null, 4); // Formatta il JSON con indentazione di 4 spazi
|
||||
|
||||
callback_menu = [];
|
||||
for (let i = 0; i < workflows.length; i++) {
|
||||
callback_menu.push("custom_workflows.assignNodeToWorkflowCallback");
|
||||
|
||||
}
|
||||
const callbackString = JSON.stringify(callback_menu, null, 4);
|
||||
|
||||
|
||||
customMenuWorkflow = `
|
||||
{
|
||||
"menuctx_workflow": "Context Menu",
|
||||
@@ -185,6 +221,8 @@ var custom_workflows = (function () {
|
||||
|
||||
],
|
||||
"menuctx_sub_opt_callback": [
|
||||
[
|
||||
],
|
||||
[
|
||||
],
|
||||
[
|
||||
@@ -196,48 +234,78 @@ var custom_workflows = (function () {
|
||||
{
|
||||
"menuctx_workflow": "Context Menu",
|
||||
"menuctx_options": [
|
||||
"Open in New Tab",
|
||||
"Delete from Category",
|
||||
"Rename Workflow",
|
||||
"Delete Workflow"
|
||||
"Delete Workflow",
|
||||
"Add to"
|
||||
|
||||
],
|
||||
"menuctx_subOptions": [
|
||||
[
|
||||
]
|
||||
],
|
||||
"menuctx_opt_callback": [
|
||||
"custom_workflows.removeNodeFromWorkflowCallback",
|
||||
"custom_workflows.renameWorkflowCallback",
|
||||
"custom_workflows.removeWorkflowCallback"
|
||||
|
||||
],
|
||||
"menuctx_sub_opt_callback": [
|
||||
],
|
||||
[
|
||||
],
|
||||
[
|
||||
]
|
||||
],
|
||||
[
|
||||
],
|
||||
${workflowsString}
|
||||
],
|
||||
"menuctx_opt_callback": [
|
||||
"custom_workflows.openWorkflowInNewTabCallback",
|
||||
"custom_workflows.removeNodeFromWorkflowCallback",
|
||||
"custom_workflows.renameWorkflowCallback",
|
||||
"custom_workflows.removeWorkflowCallback"
|
||||
|
||||
|
||||
],
|
||||
"menuctx_sub_opt_callback": [
|
||||
[
|
||||
],
|
||||
[
|
||||
],
|
||||
[
|
||||
],
|
||||
[
|
||||
],
|
||||
${callbackString}
|
||||
]
|
||||
}`;
|
||||
customMenuWorkflowItemOut = `
|
||||
{
|
||||
"menuctx_workflow": "Context Menu",
|
||||
"menuctx_options": [
|
||||
"Open in New Tab",
|
||||
"Rename Workflow",
|
||||
"Delete Workflow"
|
||||
"Delete Workflow",
|
||||
"Add to"
|
||||
|
||||
],
|
||||
"menuctx_subOptions": [
|
||||
[
|
||||
]
|
||||
],
|
||||
"menuctx_opt_callback": [
|
||||
"custom_workflows.renameWorkflowCallback",
|
||||
"custom_workflows.removeWorkflowCallback"
|
||||
|
||||
],
|
||||
"menuctx_sub_opt_callback": [
|
||||
],
|
||||
[
|
||||
],
|
||||
[
|
||||
]
|
||||
],
|
||||
${workflowsString}
|
||||
],
|
||||
"menuctx_opt_callback": [
|
||||
"custom_workflows.openWorkflowInNewTabCallback",
|
||||
"custom_workflows.renameWorkflowCallback",
|
||||
"custom_workflows.removeWorkflowCallback"
|
||||
|
||||
|
||||
],
|
||||
"menuctx_sub_opt_callback": [
|
||||
[
|
||||
],
|
||||
[
|
||||
],
|
||||
[
|
||||
],
|
||||
${callbackString}
|
||||
]
|
||||
}`;
|
||||
|
||||
@@ -313,10 +381,10 @@ var custom_workflows = (function () {
|
||||
async function handleDocumentDropEvent(event) {
|
||||
//prevent default action
|
||||
event.preventDefault();
|
||||
const sb_workflow_replace = JSON.parse(await getConfiguration('sb_workflow_replace', true))
|
||||
const sb_workflow_replace = JSON.parse(await getConfiguration('sb_workflow_replace', true)) || true;
|
||||
if (draggedElement.id === 'sidebarWorkflowItem' && event.target.id === 'graph-canvas') {
|
||||
|
||||
|
||||
|
||||
if (draggedElement.dataset.data === 'NaN') { return; }
|
||||
if (sb_workflow_replace) {
|
||||
const confirmation = confirm("This will replace the current workflow. Are you sure?");
|
||||
if (!confirmation) { return; }
|
||||
@@ -333,10 +401,12 @@ var custom_workflows = (function () {
|
||||
.then(response => 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");
|
||||
custom_workflows.renderList("custom_workflows_main");
|
||||
custom_workflows.checkAndLoadWorkflow();
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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"
|
||||
]
|
||||
|
||||
+1
-1
@@ -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]
|
||||
|
||||
Reference in New Issue
Block a user