diff --git a/endpoint.py b/endpoint.py index bb140c0..6fda049 100644 --- a/endpoint.py +++ b/endpoint.py @@ -1,6 +1,6 @@ import csv import os -import random +import secrets import sys from pathlib import Path from typing import Any @@ -56,7 +56,13 @@ def ACTIONS_installDependency(dependency_names=None): # break -def ACTIONS_getUserImages(mode: str, count=200, offset=0): +def ACTIONS_getUserImages( + mode: str, + count=200, + offset=0, + sort: str | None = None, + include_subfolders: bool = False, +): # TODO: find a better name :s enabled = "MTB_EXPOSE" in os.environ if not enabled: @@ -64,13 +70,45 @@ def ACTIONS_getUserImages(mode: str, count=200, offset=0): imgs = {} entry_dir = input_dir if mode == "input" else output_dir - for i, img in enumerate(entry_dir.glob("*.png")): + pattern = "**/*.png" if include_subfolders else "*.png" + + entry_gen = entry_dir.glob(pattern) + + entries = {} + + if sort: + sort = sort.lower() + if sort == "none": + entries = entry_gen + elif sort == "modified": + entries = sorted( + entry_gen, key=lambda x: x.stat().st_mtime, reverse=True + ) + elif sort == "modified-reverse": + entries = sorted(entry_gen, key=lambda x: x.stat().st_mtime) + elif sort == "name": + entries = sorted(entry_gen, key=lambda x: x.name) + elif sort == "name-reverse": + entries = sorted(entry_gen, key=lambda x: x.name, reverse=True) + else: + endlog.warning(f"Sort mode {sort} not supported") + entries = entry_gen + else: + entries = entry_gen + + for i, img in enumerate(entries): if i < offset: continue - imgs[img.stem] = ( - f"/mtb/view?filename={img.name}&width=512&type={mode}&subfolder=&preview=&rand={random.random()}" + + subfolder = ( + img.parent.relative_to(entry_dir) if include_subfolders else "" ) - if i >= count: + imgs[img.stem] = ( + f"/mtb/view?filename={img.name}&width=512&type={mode}&subfolder=" + f"{subfolder}" + f"&preview=&rand={secrets.randbelow(424242)}" + ) + if i >= count + offset - 1: break return imgs diff --git a/web/imageFeed.js b/web/imageFeed.js index 626fec6..68d2a69 100644 --- a/web/imageFeed.js +++ b/web/imageFeed.js @@ -13,40 +13,40 @@ import { api } from '../../scripts/api.js' import { app } from '../../scripts/app.js' import { LocalStorageManager } from './comfy_shared.js' const styles = { - lighbox: { - position: 'fixed', - top: 0, - left: 0, - width: '100vw', - height: '100vh', - background: 'rgba(0,0,0,0.5)', - display: 'none', - justifyContent: 'center', - alignItems: 'center', - zIndex: 999, - }, - lightboxBtn: (extra) => ({ - position: 'absolute', - top: '50%', - background: 'none', - border: 'none', - color: '#fff', - zIndex: 1000, - fontSize: '30px', - cursor: 'pointer', - pointerEvents: 'auto', - ...extra, - }), - img_list: { - minHeight: '30px', - maxHeight: '300px', - width: '100vw', - position: 'absolute', - bottom: 0, - zIndex: 10, - background: '#333', - overflow: 'auto', - }, + lighbox: { + position: 'fixed', + top: 0, + left: 0, + width: '100vw', + height: '100vh', + background: 'rgba(0,0,0,0.5)', + display: 'none', + justifyContent: 'center', + alignItems: 'center', + zIndex: 999, + }, + lightboxBtn: (extra) => ({ + position: 'absolute', + top: '50%', + background: 'none', + border: 'none', + color: '#fff', + zIndex: 1000, + fontSize: '30px', + cursor: 'pointer', + pointerEvents: 'auto', + ...extra, + }), + img_list: { + minHeight: '30px', + maxHeight: '300px', + width: '100vw', + position: 'absolute', + bottom: 0, + zIndex: 10, + background: '#333', + overflow: 'auto', + }, } let currentImageIndex = 0 @@ -58,298 +58,299 @@ const storage = new LocalStorageManager('mtb') let activated = storage.get('image_feed', false) app.registerExtension({ - name: 'mtb.ImageFeed', - setup: () => { - app.ui.settings.addSetting({ - id: 'mtb.imageFeed.enabled', - name: '[⚡mtb] Enable image feed', - type: 'boolean', - defaultValue: true, - attrs: { - style: { - fontFamily: 'monospace', - }, - }, - async onChange(value) { - storage.set('image_feed', value) - activated = value - }, - }) - }, - init: async () => { - if (!activated) { - return - } - const pythongossFeed = app.extensions.find( - (e) => e.name === 'pysssss.ImageFeed', - ) - if (pythongossFeed) { - console.warn( - "[mtb] - Aborting the loading of mtb's imageFeed in favor of pysssss.ImageFeed", - ) - activated = false // just in case other methods are added later on - return - } - // - HTML & CSS - //- lightbox - const lightboxContainer = document.createElement('div') - Object.assign(lightboxContainer.style, styles.lighbox) + name: 'mtb.ImageFeed', + setup: () => { + app.ui.settings.addSetting({ + id: 'mtb.Main.image-feed-enabled', + category: ['mtb', 'Main', 'image-feed-enabled'], + name: 'Enable Image Feed', + type: 'boolean', + defaultValue: false, + attrs: { + style: { + fontFamily: 'monospace', + }, + }, + async onChange(value) { + storage.set('image_feed', value) + activated = value + }, + }) + }, + init: async () => { + if (!activated) { + return + } + const pythongossFeed = app.extensions.find( + (e) => e.name === 'pysssss.ImageFeed', + ) + if (pythongossFeed) { + console.warn( + "[mtb] - Aborting the loading of mtb's imageFeed in favor of pysssss.ImageFeed", + ) + activated = false // just in case other methods are added later on + return + } + // - HTML & CSS + //- lightbox + const lightboxContainer = document.createElement('div') + Object.assign(lightboxContainer.style, styles.lighbox) - const lightboxImage = document.createElement('img') - Object.assign(lightboxImage.style, { - maxHeight: '100%', - maxWidth: '100%', - borderRadius: '5px', - }) + const lightboxImage = document.createElement('img') + Object.assign(lightboxImage.style, { + maxHeight: '100%', + maxWidth: '100%', + borderRadius: '5px', + }) - // previous and next buttons - const lightboxPrevBtn = document.createElement('button') - const lightboxNextBtn = document.createElement('button') + // previous and next buttons + const lightboxPrevBtn = document.createElement('button') + const lightboxNextBtn = document.createElement('button') - lightboxPrevBtn.textContent = '❮' - lightboxNextBtn.textContent = '❯' + lightboxPrevBtn.textContent = '❮' + lightboxNextBtn.textContent = '❯' - Object.assign(lightboxPrevBtn.style, styles.lightboxBtn({ left: '0%' })) - Object.assign(lightboxNextBtn.style, styles.lightboxBtn({ right: '0%' })) + Object.assign(lightboxPrevBtn.style, styles.lightboxBtn({ left: '0%' })) + Object.assign(lightboxNextBtn.style, styles.lightboxBtn({ right: '0%' })) - // close button - const lightboxCloseBtn = document.createElement('button') - Object.assign( - lightboxCloseBtn.style, - styles.lightboxBtn({ right: '0', top: '0' }), - ) - lightboxCloseBtn.textContent = '❌' + // close button + const lightboxCloseBtn = document.createElement('button') + Object.assign( + lightboxCloseBtn.style, + styles.lightboxBtn({ right: '0', top: '0' }), + ) + lightboxCloseBtn.textContent = '❌' - const lightboxButtons = document.createElement('div') - Object.assign(lightboxButtons.style, { - position: 'absolute', - top: '0%', - right: '0%', - // transform: "translate(50%, -50%)", - height: '100%', - width: '100%', - background: 'none', - border: 'none', - color: '#fff', - fontSize: '30px', - cursor: 'pointer', - pointerEvents: 'none', - }) + const lightboxButtons = document.createElement('div') + Object.assign(lightboxButtons.style, { + position: 'absolute', + top: '0%', + right: '0%', + // transform: "translate(50%, -50%)", + height: '100%', + width: '100%', + background: 'none', + border: 'none', + color: '#fff', + fontSize: '30px', + cursor: 'pointer', + pointerEvents: 'none', + }) - lightboxButtons.append(lightboxPrevBtn, lightboxNextBtn, lightboxCloseBtn) - lightboxContainer.append(lightboxButtons, lightboxImage) + lightboxButtons.append(lightboxPrevBtn, lightboxNextBtn, lightboxCloseBtn) + lightboxContainer.append(lightboxButtons, lightboxImage) - //- image list - const imageListContainer = document.createElement('div') - Object.assign(imageListContainer.style, styles.img_list) + //- image list + const imageListContainer = document.createElement('div') + Object.assign(imageListContainer.style, styles.img_list) - const createImgListBtn = (text, style) => { - const btn = document.createElement('button') - btn.type = 'button' - btn.textContent = text - Object.assign(btn.style, { - ...style, - border: 'none', - color: '#fff', - background: 'none', - height: '20px', - cursor: 'pointer', - position: 'absolute', - top: '5px', - fontSize: '12px', - lineHeight: '12px', - }) - imageListContainer.append(btn) - return btn - } - const showBtn = document.createElement('button') - const closeBtn = createImgListBtn('❌', { - width: '20px', - textIndent: '-4px', - right: '5px', - }) - const loadButton = createImgListBtn('Load Session History', { - right: '90px', - }) - const clearButton = createImgListBtn('Clear', { - right: '30px', - }) + const createImgListBtn = (text, style) => { + const btn = document.createElement('button') + btn.type = 'button' + btn.textContent = text + Object.assign(btn.style, { + ...style, + border: 'none', + color: '#fff', + background: 'none', + height: '20px', + cursor: 'pointer', + position: 'absolute', + top: '5px', + fontSize: '12px', + lineHeight: '12px', + }) + imageListContainer.append(btn) + return btn + } + const showBtn = document.createElement('button') + const closeBtn = createImgListBtn('❌', { + width: '20px', + textIndent: '-4px', + right: '5px', + }) + const loadButton = createImgListBtn('Load Session History', { + right: '90px', + }) + const clearButton = createImgListBtn('Clear', { + right: '30px', + }) - //- tools popup button - showBtn.classList.add('comfy-settings-btn') - Object.assign(showBtn.style, { - right: '16px', - cursor: 'pointer', - display: 'none', - }) + //- tools popup button + showBtn.classList.add('comfy-settings-btn') + Object.assign(showBtn.style, { + right: '16px', + cursor: 'pointer', + display: 'none', + }) - //- append to DOM - document.body.append(imageListContainer) + //- append to DOM + document.body.append(imageListContainer) - showBtn.textContent = '🖼' - showBtn.onclick = () => { - imageListContainer.style.display = 'block' - showBtn.style.display = 'none' - } - document.querySelector('.comfy-settings-btn').after(showBtn) - document.querySelector('.comfy-settings-btn').after(lightboxContainer) + showBtn.textContent = '🖼' + showBtn.onclick = () => { + imageListContainer.style.display = 'block' + showBtn.style.display = 'none' + } + document.querySelector('.comfy-settings-btn').after(showBtn) + document.querySelector('.comfy-settings-btn').after(lightboxContainer) - // for (const { output } of history) { - // if (output?.images) { - // for (const src of output.images) { - // const img = document.createElement("img"); - // const but = document.createElement("button"); + // for (const { output } of history) { + // if (output?.images) { + // for (const src of output.images) { + // const img = document.createElement("img"); + // const but = document.createElement("button"); - //- callbacks - closeBtn.onclick = () => { - imageListContainer.style.display = 'none' - showBtn.style.display = 'unset' - } + //- callbacks + closeBtn.onclick = () => { + imageListContainer.style.display = 'none' + showBtn.style.display = 'unset' + } - clearButton.onclick = () => { - imageListContainer.replaceChildren(closeBtn, clearButton, loadButton) - } + clearButton.onclick = () => { + imageListContainer.replaceChildren(closeBtn, clearButton, loadButton) + } - lightboxNextBtn.onclick = () => { - currentImageIndex = (currentImageIndex + 1) % imageUrls.length - const imageUrl = imageUrls[currentImageIndex] - lightboxImage.src = imageUrl - } + lightboxNextBtn.onclick = () => { + currentImageIndex = (currentImageIndex + 1) % imageUrls.length + const imageUrl = imageUrls[currentImageIndex] + lightboxImage.src = imageUrl + } - // Modify the lightboxPrevBtn onclick callback - lightboxPrevBtn.onclick = () => { - currentImageIndex = - (currentImageIndex - 1 + imageUrls.length) % imageUrls.length - const imageUrl = imageUrls[currentImageIndex] - lightboxImage.src = imageUrl - } + // Modify the lightboxPrevBtn onclick callback + lightboxPrevBtn.onclick = () => { + currentImageIndex = + (currentImageIndex - 1 + imageUrls.length) % imageUrls.length + const imageUrl = imageUrls[currentImageIndex] + lightboxImage.src = imageUrl + } - lightboxCloseBtn.onclick = () => { - lightboxContainer.style.display = 'none' - } - lightboxImage.onclick = lightboxNextBtn.onclick - /** - * This is the function that creates the image buttons for the image list - * They are wrapped in a button so that they can be clicked and open - * the image in the lightbox. - * @param {*} src - */ - const createImageBtn = (src) => { - console.debug(`making image ${src.filename}`) - const img = document.createElement('img') - const but = document.createElement('button') + lightboxCloseBtn.onclick = () => { + lightboxContainer.style.display = 'none' + } + lightboxImage.onclick = lightboxNextBtn.onclick + /** + * This is the function that creates the image buttons for the image list + * They are wrapped in a button so that they can be clicked and open + * the image in the lightbox. + * @param {*} src + */ + const createImageBtn = (src) => { + console.debug(`making image ${src.filename}`) + const img = document.createElement('img') + const but = document.createElement('button') - Object.assign(but.style, { - height: '120px', - width: '120px', - border: 'none', - padding: 0, - margin: 0, - }) - Object.assign(img.style, { - width: '100%', - height: '100%', - objectFit: 'cover', - }) + Object.assign(but.style, { + height: '120px', + width: '120px', + border: 'none', + padding: 0, + margin: 0, + }) + Object.assign(img.style, { + width: '100%', + height: '100%', + objectFit: 'cover', + }) - img.src = `/view?filename=${encodeURIComponent(src.filename)}&type=${ - src.type - }&subfolder=${encodeURIComponent(src.subfolder)}` + img.src = `/view?filename=${encodeURIComponent(src.filename)}&type=${ + src.type + }&subfolder=${encodeURIComponent(src.subfolder)}` - imageUrls.push(img.src) + imageUrls.push(img.src) - console.debug(img.src) + console.debug(img.src) - img.onload = () => { - but.style.width = `${120 * (img.naturalWidth / img.naturalHeight)}px` - } + img.onload = () => { + but.style.width = `${120 * (img.naturalWidth / img.naturalHeight)}px` + } - but.onclick = () => { - lightboxContainer.style.display = 'flex' - // add the same image to the lightbox - lightboxImage.src = img.src - // lighboxContainer.replaceChildren(lightboxButtons, img); - } + but.onclick = () => { + lightboxContainer.style.display = 'flex' + // add the same image to the lightbox + lightboxImage.src = img.src + // lighboxContainer.replaceChildren(lightboxButtons, img); + } - // add right click menu - but.addEventListener('contextmenu', (e) => { - e.preventDefault() + // add right click menu + but.addEventListener('contextmenu', (e) => { + e.preventDefault() - if (image_menu) { - image_menu.remove() - } + if (image_menu) { + image_menu.remove() + } - image_menu = document.createElement('div') - Object.assign(image_menu.style, { - position: 'absolute', - top: `${e.clientY}px`, - left: `${e.clientX}px`, - background: '#333', - color: '#fff', - padding: '5px', - borderRadius: '5px', - zIndex: 999, - }) - const load_img = document.createElement('button') - load_img.textContent = 'Load' - load_img.onclick = () => { - app.handleFile(img.src) - } + image_menu = document.createElement('div') + Object.assign(image_menu.style, { + position: 'absolute', + top: `${e.clientY}px`, + left: `${e.clientX}px`, + background: '#333', + color: '#fff', + padding: '5px', + borderRadius: '5px', + zIndex: 999, + }) + const load_img = document.createElement('button') + load_img.textContent = 'Load' + load_img.onclick = () => { + app.handleFile(img.src) + } - image_menu.appendChild(load_img) - document.body.appendChild(image_menu) - }) + image_menu.appendChild(load_img) + document.body.appendChild(image_menu) + }) - but.append(img) - imageListContainer.prepend(but) - } + but.append(img) + imageListContainer.prepend(but) + } - loadButton.onclick = async () => { - const all_history = await api.getHistory() - for (const history of all_history.History) { - if (history.outputs) { - for (const key of Object.keys(history.outputs)) { - console.debug(key) - if (history.outputs[key].images) { - for (const im of history.outputs[key].images) { - console.debug(im) - createImageBtn(im) - } - } - } - // for (const src of outputs.outputs.images) { - // console.debug(src) - // makeImage(`${src.subfolder}/${src.filename}`) - // } - } - } - } + loadButton.onclick = async () => { + const all_history = await api.getHistory() + for (const history of all_history.History) { + if (history.outputs) { + for (const key of Object.keys(history.outputs)) { + console.debug(key) + if (history.outputs[key].images) { + for (const im of history.outputs[key].images) { + console.debug(im) + createImageBtn(im) + } + } + } + // for (const src of outputs.outputs.images) { + // console.debug(src) + // makeImage(`${src.subfolder}/${src.filename}`) + // } + } + } + } - ///////------- + ///////------- - // const all_history = await api.getHistory() - // for (const history of all_history.History) { - // if (history.outputs) { - // for (const key of Object.keys(history.outputs)) { - // for (const im of history.outputs[key].images) { - // makeImage(im) - // } - // } - // // for (const src of outputs.outputs.images) { - // // console.debug(src) - // // makeImage(`${src.subfolder}/${src.filename}`) - // // } - // } - // } + // const all_history = await api.getHistory() + // for (const history of all_history.History) { + // if (history.outputs) { + // for (const key of Object.keys(history.outputs)) { + // for (const im of history.outputs[key].images) { + // makeImage(im) + // } + // } + // // for (const src of outputs.outputs.images) { + // // console.debug(src) + // // makeImage(`${src.subfolder}/${src.filename}`) + // // } + // } + // } - //- Hook into the API - api.addEventListener('executed', ({ detail }) => { - if (detail?.output?.images) { - for (const src of detail.output.images) { - console.debug(`Adding ${src} to image feed`) - createImageBtn(src) - } - } - }) - }, + //- Hook into the API + api.addEventListener('executed', ({ detail }) => { + if (detail?.output?.images) { + for (const src of detail.output.images) { + console.debug(`Adding ${src} to image feed`) + createImageBtn(src) + } + } + }) + }, }) diff --git a/web/mtb_input_output_sidebar.js b/web/mtb_input_output_sidebar.js index 6b17417..ad60d53 100644 --- a/web/mtb_input_output_sidebar.js +++ b/web/mtb_input_output_sidebar.js @@ -1,10 +1,10 @@ import { app } from '../../scripts/app.js' import { api } from '../../scripts/api.js' -import * as shared from './comfy_shared.js' +// import * as shared from './comfy_shared.js' import { - defineCSSClass, + // defineCSSClass, ensureMTBStyles, makeElement, makeSelect, @@ -12,8 +12,10 @@ import { renderSidebar, } from './mtb_ui.js' +let offset = 0 let currentWidth = 200 let currentMode = 'input' +let currentSort = 'None' const IMAGE_NODES = ['LoadImage'] @@ -37,11 +39,34 @@ const getImgsFromUrls = (urls, target) => { const a = makeElement('img') a.src = url a.width = currentWidth - a.onclick = (e) => { + if (currentMode === 'input') { + a.onclick = (_e) => { + const selected = app.canvas.selected_nodes + if (selected && Object.keys(selected).length === 0) { + app.extensionManager.toast.add({ + severity: 'warn', + summary: 'No LoadImage node selected!', + detail: + 'For now the only action when clicking images in the sidebar is to set the image on all selected LoadImage nodes.', + life: 5000, + }) + return + } - for (const [_id, node] of Object.entries(app.canvas.selected_nodes)) { - updateImage(node, `${key}.png`) + for (const [_id, node] of Object.entries(app.canvas.selected_nodes)) { + updateImage(node, `${key}.png`) + } } + } else { + a.onclick = (_e) => + // window.MTB?.notify?.("Output import isn't supported yet...", 5000) + app.extensionManager.toast.add({ + severity: 'warn', + summary: 'Outputs not supported', + detail: + 'For now only inputs can be clicked to load the image on the active LoadImage node.', + life: 5000, + }) } imgs.push(a) } @@ -51,12 +76,15 @@ const getImgsFromUrls = (urls, target) => { return imgs } -const getUrls = async (mode) => { +const getUrls = async () => { + const count = await api.getSetting('mtb.io-sidebar.count') + console.log('Sidebar count', count) const inputs = await api.fetchApi('/mtb/actions', { method: 'POST', body: JSON.stringify({ name: 'getUserImages', - args: [mode], + // mode, count, offset + args: [currentMode, count, offset, currentSort], }), }) const output = await inputs.json() @@ -70,11 +98,59 @@ if (window?.__COMFYUI_FRONTEND_VERSION__) { ensureMTBStyles() + app.ui.settings.addSetting({ + id: 'mtb.io-sidebar.count', + category: ['mtb', 'Input & Output Sidebar', 'count'], + + name: 'Number of images to fetch', + type: 'number', + defaultValue: 1000, + + tooltip: + "This setting affects the input/output sidebar to determine how many images to fetch per pagination (pagination is not yet supported so for now it's the static total)", + attrs: { + style: { + // fontFamily: 'monospace', + }, + }, + }) + + app.ui.settings.addSetting({ + id: 'mtb.io-sidebar.img-size', + category: ['mtb', 'Input & Output Sidebar', 'img-size'], + + name: 'Resolution of the images', + type: 'number', + defaultValue: 512, + + tooltip: "It's recommended to keep it at 512px", + attrs: { + style: { + // fontFamily: 'monospace', + }, + }, + }) + app.ui.settings.addSetting({ + id: 'mtb.io-sidebar.sort', + category: ['mtb', 'Input & Output Sidebar', 'sort'], + name: 'Default sort mode', + type: 'combo', + + onChange: (v) => { + // alert(`Sort is now ${v}`) + currentSort = v + }, + + defaultValue: 'Modified', + // tooltip: "It's recommended to keep it at 512px", + options: ['None', 'Modified', 'Modified-Reverse', 'Name', 'Name-Reverse'], + }) + app.extensionManager.registerSidebarTab({ id: 'mtb-inputs-outputs', icon: 'pi pi-images', title: 'Input & Outputs', - tooltip: 'Browse inputs and outputs directories.', + tooltip: 'MTB: Browse inputs and outputs directories.', type: 'custom', // this is run everytime the tab's diplay is toggled on. @@ -102,7 +178,7 @@ if (window?.__COMFYUI_FRONTEND_VERSION__) { currentMode = newMode if (changed) { imgGrid.innerHTML = '' - const urls = await getUrls(currentMode) + const urls = await getUrls() if (urls) { imgs = getImgsFromUrls(urls, imgGrid) } @@ -110,7 +186,27 @@ if (window?.__COMFYUI_FRONTEND_VERSION__) { }) const imgTools = makeElement('div.mtb_tools') + const orderSelect = makeSelect( + ['None', 'Modified', 'Modified-Reverse', 'Name', 'Name-Reverse'], + currentSort, + ) + + orderSelect.addEventListener('change', async (e) => { + const newSort = e.target.value + const changed = newSort !== currentSort + currentSort = newSort + if (changed) { + imgGrid.innerHTML = '' + const urls = await getUrls() + if (urls) { + imgs = getImgsFromUrls(urls, imgGrid) + } + } + }) + const sizeSlider = makeSlider(64, 1024, currentWidth, 1) + imgTools.appendChild(orderSelect) + imgTools.appendChild(sizeSlider) imgs = getImgsFromUrls(urls, imgGrid) diff --git a/web/mtb_sidebar.js b/web/mtb_sidebar.js index 5dec40b..2f652cc 100644 --- a/web/mtb_sidebar.js +++ b/web/mtb_sidebar.js @@ -1,5 +1,5 @@ import { app } from '../../scripts/app.js' -import { api } from '../../scripts/api.js' +// import { api } from '../../scripts/api.js' import * as shared from './comfy_shared.js' import { createOutliner } from './dist/mtb_inspector.js' @@ -12,7 +12,7 @@ if (window?.__COMFYUI_FRONTEND_VERSION__) { id: 'mtb-nodes', icon: 'pi pi-bolt', title: 'MTB', - tooltip: 'sidebar for mtb nodes', + tooltip: 'MTB: API outliner', type: 'custom', // this is run everytime the tab's diplay is toggled on. render: (el) => { diff --git a/web/mtb_ui.js b/web/mtb_ui.js index 901ec0e..1b8e6d5 100644 --- a/web/mtb_ui.js +++ b/web/mtb_ui.js @@ -263,13 +263,13 @@ const camelToKebab = (prop) => */ const parseStyleString = (styleString) => { const styleObj = {} - styleString.split(';').forEach((rule) => { + for (const rule of styleString.split(';')) { const [property, value] = rule.split(':').map((item) => item.trim()) if (property && value) { const camelProp = property.replace(/-([a-z])/g, (g) => g[1].toUpperCase()) styleObj[camelProp] = value } - }) + } return styleObj } diff --git a/web/mtb_widgets.js b/web/mtb_widgets.js index 73640cb..2aa364c 100644 --- a/web/mtb_widgets.js +++ b/web/mtb_widgets.js @@ -669,28 +669,29 @@ const mtb_widgets = { } }, - setup: () => { - app.ui.settings.addSetting({ - id: 'mtb.Debug.enabled', - name: '[⚡mtb] Enable Debug (py and js)', - type: 'boolean', - defaultValue: false, + setup: () => { + app.ui.settings.addSetting({ + id: 'mtb.Main.debug-enabled', + category: ['mtb', 'Main', 'debug-enabled'], + name: 'Enable Debug (py and js)', + type: 'boolean', + defaultValue: false, - tooltip: - 'This will enable debug messages in the console and in the python console respectively', - attrs: { - style: { - fontFamily: 'monospace', - }, - }, - async onChange(value) { - if (!window.MTB) { - window.MTB = {} - } - window.MTB.DEBUG = value - if (value) { - infoLogger('Enabled DEBUG mode') - } + tooltip: + 'This will enable debug messages in the console and in the python console respectively, no need to restart the server, but do reload the webui', + attrs: { + style: { + // fontFamily: 'monospace', + }, + }, + async onChange(value) { + if (!window.MTB) { + window.MTB = {} + } + window.MTB.DEBUG = value + if (value) { + infoLogger('Enabled DEBUG mode') + } await api .fetchApi('/mtb/debug', {