From a8ac7296c24b9fe3244c9a4bb0553a4e7f2ae205 Mon Sep 17 00:00:00 2001 From: Austin Mroz Date: Mon, 30 Sep 2024 14:40:54 -0500 Subject: [PATCH] Theming, pruning, and optional callbacks Basic styling has been added to the display of documentation for nodes using the existing tooltip system. This will need another pass to ensure that style updates immediately when the light/dark toggle is hit instead of requiring a change of node. VHS specific namings have been replaced and the code for determining what the mouse is hovering over has been removed. The existing tooltip implementation is cleaner and will need to be integrated anyways so tooltips are temporarily suppressed for the node actively being displayed in the documentation sidebar. Optional callbacks have been added for the initial sidebar display and a user selecting a node element by hovering over it. While selection is not yet implemented, this should cover any developer needs from more involved collapsables to automated seeking to video timestamps. --- src/extensions/core/documentationSidebar.ts | 150 ++++++++------------ 1 file changed, 63 insertions(+), 87 deletions(-) diff --git a/src/extensions/core/documentationSidebar.ts b/src/extensions/core/documentationSidebar.ts index ba49d8f1f..3635c4675 100644 --- a/src/extensions/core/documentationSidebar.ts +++ b/src/extensions/core/documentationSidebar.ts @@ -1,12 +1,8 @@ -import { app } from '../../scripts/app.js' -import { api } from '../../scripts/api.js' +import { app } from '../../scripts/app' +import { api } from '../../scripts/api' +import { getColorPalette } from './colorPalette' -let iconOverride = document.createElement('style') -iconOverride.innerHTML = `.VHSTestIcon:before {font-size: 1.5em; content: '?';}` -document.body.append(iconOverride) - -var helpDOM -helpDOM = document.createElement('div') +var helpDOM = document.createElement('div') function setCollapse(el, doCollapse) { if (doCollapse) { @@ -44,10 +40,16 @@ helpDOM.collapseOnClick = function () { let doCollapse = this.children[0].innerHTML == '-' setCollapse(this.parentElement, doCollapse) } -helpDOM.selectHelp = function (name, value) { +//TODO: connect with doc tooltips +//If doc sidebar is opened, the current node should not display tooltips, +//but navigate the sidebar pane as appropriate. +helpDOM.selectHelp = function (name: string, value?: string) { + if (helpDOM.def[2].select) { + return helpDOM.def[2].select(this, name, value) + } //attempt to navigate to name in help function collapseUnlessMatch(items, t) { - var match = items.querySelector('[vhs_title="' + t + '"]') + var match = items.querySelector('[doc_title="' + t + '"]') if (!match) { for (let i of items.children) { if (i.innerHTML.slice(0, t.length + 5).includes(t)) { @@ -61,13 +63,12 @@ helpDOM.selectHelp = function (name, value) { } //For longer documentation items with fewer collapsable elements, //scroll to make sure the entirety of the selected item is visible - //This has the unfortunate side effect of trying to scroll the main - //window if the documentation windows is forcibly offscreen, - //but it's easy to simply scroll the main window back and seems to - //have no visual side effects match.scrollIntoView(false) - window.scrollTo(0, 0) - for (let i of items.querySelectorAll('.VHS_collapse')) { + //The previous floating help implementation would try to scroll the window + //itself if the display was partiall offscreen. As the sidebar documentation + //does not pan with the canvas, this should no longer be needed + //window.scrollTo(0, 0) + for (let i of items.querySelectorAll('.doc_collapse')) { if (i.contains(match)) { setCollapse(i, false) } else { @@ -81,65 +82,6 @@ helpDOM.selectHelp = function (name, value) { collapseUnlessMatch(target, value) } } -helpDOM.addHelp = function (node, nodeType, description) { - let timeout = null - chainCallback(node, 'onMouseMove', function (e, pos, canvas) { - if (timeout) { - clearTimeout(timeout) - timeout = null - } - if (helpDOM.node != this) { - return - } - timeout = setTimeout(() => { - let n = this - if ( - pos[0] > 0 && - pos[0] < n.size[0] && - pos[1] > 0 && - pos[1] < n.size[1] - ) { - //TODO: provide help specific to element clicked - let inputRows = Math.max(n.inputs?.length || 0, n.outputs?.length || 0) - if (pos[1] < LiteGraph.NODE_SLOT_HEIGHT * inputRows) { - let row = Math.floor((pos[1] - 7) / LiteGraph.NODE_SLOT_HEIGHT) - if (pos[0] < n.size[0] / 2) { - if (row < n.inputs.length) { - helpDOM.selectHelp(n.inputs[row].name) - } - } else { - if (row < n.outputs.length) { - helpDOM.selectHelp(n.outputs[row].name) - } - } - } else { - //probably widget, but widgets have variable height. - let basey = LiteGraph.NODE_SLOT_HEIGHT * inputRows + 6 - for (let w of n.widgets) { - if (w.y) { - basey = w.y - } - let wheight = LiteGraph.NODE_WIDGET_HEIGHT + 4 - if (w.computeSize) { - wheight = w.computeSize(n.size[0])[1] - } - if (pos[1] < basey + wheight) { - helpDOM.selectHelp(w.name, w.value) - break - } - basey += wheight - } - } - } - }, 500) - }) - chainCallback(node, 'onMouseLeave', function (e, pos, canvas) { - if (timeout) { - clearTimeout(timeout) - timeout = null - } - }) -} function updateNode(node) { //Always use latest node. If it lacks documentation, that should be communicated //instead of confusing users by picking a different recent node that does @@ -155,45 +97,61 @@ function updateNode(node) { //do additional parsing to prettify output and combine tooltips let content = '' if (def.description) { - content += def.description + content += '
' + def.description + '
' } let inputs = [] for (let input in def?.input?.required || {}) { if (def.input.required[input][1]?.tooltip) { - inputs.push( - '' + input + ': ' + def.input.required[input][1].tooltip - ) + inputs.push([input, def.input.required[input][1].tooltip]) } } for (let input in def?.input?.optional || {}) { if (def.input.optional[input][1]?.tooltip) { - inputs.push( - '' + input + ': ' + def.input.optional[input][1].tooltip - ) + inputs.push([input, def.input.optional[input][1].tooltip]) } } if (inputs.length) { - content += '

' + inputs.join('
') + '
' + content += '
Inputs
' + for (let [k, v] of inputs) { + content += '
' + k + '
' + v + '
' + } + //content += "" + //content += '

' + inputs.join('
') + '
' } if (def.output_tooltips) { - content += '

' + content += '
Outputs
' let outputs = def.output_name || def.output for (let i = 0; i < outputs.length; i++) { content += - '
' + outputs[i] + ': ' + def.output_tooltips[i] + '
' + '
' + + outputs[i] + + '
' + + def.output_tooltips[i] + + '
' } + //outputs += '' } if (content == '') { content = 'No documentation available' } + content = '
' + def.display_name + '
' + content helpDOM.innerHTML = content } } +let docStyleElement = document.createElement('style') +let documentationStyle = ` +.DocumentationIcon:before { + font-size: 1.5em; content: '?'; +} +` +docStyleElement.innerHTML = documentationStyle +document.body.append(docStyleElement) + var bringToFront let documentationSidebar = { id: 'documentationSidebar', title: 'Documentation', - icon: 'VHSTestIcon', + icon: 'DocumentationIcon', type: 'custom', render: (e) => { if (!bringToFront) { @@ -203,11 +161,29 @@ let documentationSidebar = { return bringToFront.apply(this, arguments) } } + //TODO: properly update colors when theme is toggled + let documentationStyle = ` + .doc-node { + font-size: 1.5em + } + .doc-section { + background-color: ${getColorPalette().colors.comfy_base['tr-odd-bg-color']} + } + .doc-item { + margin-inline-start: 1vw; + } + .DocumentationIcon:before { + font-size: 1.5em; content: '?'; + } + ` + docStyleElement.innerHTML = documentationStyle updateNode() - e.parentElement.style.overflowX = '' if (!e?.children?.length) { e.appendChild(helpDOM) } + if (helpDOM.def.description[2]?.render) { + helpDOM.def.description[2].render(e) + } } } app.extensionManager.registerSidebarTab(documentationSidebar)