On collapse, hiding is no longer done b setting a cutoff height. This ensures that portions of elements don't get displayed incorrectly and that no amount of local font size configuration can cause lines to incorrectly show. Additional fixes (such as having the scroll bar always display) have been introduced to prevent reflows from occuring which would move the element from underneath the cursor If there is a large amount of documentation and not everything collapses, the selected element (from mouseover)) is now scrolled to ensure it and it's children are visible
276 lines
10 KiB
JavaScript
276 lines
10 KiB
JavaScript
import { app } from '../../../scripts/app.js'
|
|
|
|
function chainCallback(object, property, callback) {
|
|
if (object == undefined) {
|
|
//This should not happen.
|
|
console.error("Tried to add callback to non-existant object")
|
|
return;
|
|
}
|
|
if (property in object && object[property]) {
|
|
const callback_orig = object[property]
|
|
object[property] = function () {
|
|
const r = callback_orig.apply(this, arguments);
|
|
callback.apply(this, arguments);
|
|
return r
|
|
};
|
|
} else {
|
|
object[property] = callback;
|
|
}
|
|
}
|
|
var helpDOM;
|
|
function initHelpDOM() {
|
|
let parentDOM = document.createElement("div");
|
|
document.body.appendChild(parentDOM)
|
|
parentDOM.appendChild(helpDOM)
|
|
helpDOM.className = "litegraph";
|
|
let scrollbarStyle = document.createElement('style');
|
|
scrollbarStyle.innerHTML = `
|
|
<style id="scroll-properties">
|
|
* {
|
|
scrollbar-width: 6px;
|
|
scrollbar-color: #0003 #0000;
|
|
}
|
|
::-webkit-scrollbar {
|
|
background: transparent;
|
|
width: 6px;
|
|
}
|
|
::-webkit-scrollbar-thumb {
|
|
background: #0005;
|
|
border-radius: 20px
|
|
}
|
|
::-webkit-scrollbar-button {
|
|
display: none;
|
|
}
|
|
</style>
|
|
`
|
|
parentDOM.appendChild(scrollbarStyle)
|
|
chainCallback(app.canvas, "onDrawForeground", function (ctx, visible_rect){
|
|
let n = helpDOM.node
|
|
if (!n || !n?.graph) {
|
|
parentDOM.style['left'] = '-5000px'
|
|
return
|
|
}
|
|
//draw : function(ctx, node, widgetWidth, widgetY, height) {
|
|
//update widget position, even if off screen
|
|
const transform = ctx.getTransform();
|
|
const scale = app.canvas.ds.scale;//gets the litegraph zoom
|
|
//calculate coordinates with account for browser zoom
|
|
const bcr = app.canvas.canvas.getBoundingClientRect()
|
|
const x = transform.e*scale/transform.a + bcr.x;
|
|
const y = transform.f*scale/transform.a + bcr.y;
|
|
//TODO: text reflows at low zoom. investigate alternatives
|
|
Object.assign(parentDOM.style, {
|
|
left: (x+(n.pos[0] + n.size[0]+15)*scale) + "px",
|
|
top: (y+(n.pos[1]-LiteGraph.NODE_TITLE_HEIGHT)*scale) + "px",
|
|
width: "400px",
|
|
minHeight: "100px",
|
|
maxHeight: "600px",
|
|
overflowY: 'scroll',
|
|
transformOrigin: '0 0',
|
|
transform: 'scale(' + scale + ',' + scale +')',
|
|
fontSize: '18px',
|
|
backgroundColor: LiteGraph.NODE_DEFAULT_BGCOLOR,
|
|
boxShadow: '0 0 10px black',
|
|
borderRadius: '4px',
|
|
padding: '3px',
|
|
zIndex: 3,
|
|
position: "absolute",
|
|
display: 'inline',
|
|
});
|
|
});
|
|
function setCollapse(el, doCollapse) {
|
|
if (doCollapse) {
|
|
el.children[0].innerHTML = '[+]'
|
|
Object.assign(el.children[1].style, {
|
|
color: '#CCC',
|
|
overflowX: 'hidden',
|
|
width: '0px',
|
|
minWidth: 'calc(100% - 20px)',
|
|
textOverflow: 'ellipsis',
|
|
whiteSpace: 'nowrap',
|
|
})
|
|
for (let child of el.children[1].children) {
|
|
if (child.style.display != 'none'){
|
|
child.origDisplay = child.style.display
|
|
}
|
|
child.style.display = 'none'
|
|
}
|
|
} else {
|
|
el.children[0].innerHTML = '[-]'
|
|
Object.assign(el.children[1].style, {
|
|
color: '',
|
|
overflowX: '',
|
|
width: '100%',
|
|
minWidth: '',
|
|
textOverflow: '',
|
|
whiteSpace: '',
|
|
})
|
|
for (let child of el.children[1].children) {
|
|
child.style.display = child.origDisplay
|
|
}
|
|
}
|
|
}
|
|
helpDOM.collapseOnClick = function() {
|
|
let doCollapse = this.innerHTML[1] == '-'
|
|
setCollapse(this.parentElement, doCollapse)
|
|
}
|
|
helpDOM.selectHelp = function(name, value) {
|
|
//attempt to navigate to name in help
|
|
function collapseUnlessMatch(items,t) {
|
|
var match = items.querySelector('[vhs_title="' + t + '"]')
|
|
if (!match) {
|
|
for (let i of items.children) {
|
|
if (i.innerHTML.slice(0,t.length+5).includes(t)) {
|
|
match = i
|
|
break
|
|
}
|
|
}
|
|
}
|
|
if (!match) {
|
|
return null
|
|
}
|
|
match.scrollIntoView(false)
|
|
for (let i of items.querySelectorAll('.VHS_collapse')) {
|
|
if (i.contains(match)) {
|
|
setCollapse(i, false)
|
|
} else {
|
|
setCollapse(i, true)
|
|
}
|
|
}
|
|
return match
|
|
}
|
|
let target = collapseUnlessMatch(helpDOM, name)
|
|
if (target && value) {
|
|
collapseUnlessMatch(target, value)
|
|
}
|
|
}
|
|
|
|
helpDOM.addHelp = function(node, nodeType, description) {
|
|
if (!description) {
|
|
return
|
|
}
|
|
//Pad computed size for the clickable question mark
|
|
let originalComputeSize = node.computeSize
|
|
node.computeSize = function() {
|
|
let size = originalComputeSize.apply(this, arguments)
|
|
if (!this.title) {
|
|
return size
|
|
}
|
|
let title_width = this.title.length * 0.6 * LiteGraph.NODE_TEXT_SIZE
|
|
size[0] = Math.max(size[0], title_width + LiteGraph.NODE_TITLE_HEIGHT)
|
|
return size
|
|
}
|
|
|
|
node.description = description
|
|
chainCallback(node, "onDrawForeground", function (ctx) {
|
|
//draw question mark
|
|
ctx.save()
|
|
ctx.font = 'bold 20px Arial'
|
|
ctx.fillText("?", this.size[0]-17, -8)
|
|
ctx.restore()
|
|
})
|
|
chainCallback(node, "onMouseDown", function (e, pos, canvas) {
|
|
//On click would be preferred, but this'll be good enough
|
|
if (pos[1] < 0 && pos[0] + LiteGraph.NODE_TITLE_HEIGHT > this.size[0]) {
|
|
//corner question mark clicked
|
|
if (helpDOM.node == this) {
|
|
helpDOM.node = undefined
|
|
} else {
|
|
helpDOM.node = this;
|
|
helpDOM.innerHTML = this.description || "no help provided ".repeat(20)
|
|
for (let e of helpDOM.querySelectorAll('.VHS_collapse')) {
|
|
e.children[0].onclick = helpDOM.collapseOnClick
|
|
e.children[0].style.cursor = 'pointer'
|
|
}
|
|
for (let e of helpDOM.querySelectorAll('.VHS_precollapse')) {
|
|
setCollapse(e, true)
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
})
|
|
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, n.outputs.length)
|
|
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.
|
|
for (let w of n.widgets) {
|
|
let wheight = LiteGraph.NODE_WIDGET_HEIGHT
|
|
if (w.computeSize) {
|
|
wheight = w.computeSize(n.size[0])
|
|
}
|
|
if (pos[1] < w.y + wheight) {
|
|
helpDOM.selectHelp(w.name, w.value)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}, 500)
|
|
})
|
|
chainCallback(node, "onMouseLeave", function (e, pos, canvas) {
|
|
if (timeout) {
|
|
clearTimeout(timeout)
|
|
timeout = null
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
|
|
|
|
app.registerExtension({
|
|
name: "AnimateDiffEvolved.documentation",
|
|
async init() {
|
|
if (app.VHSHelp) {
|
|
helpDOM = app.VHSHelp
|
|
} else {
|
|
helpDOM = document.createElement("div");
|
|
initHelpDOM()
|
|
app.VHSHelp = helpDOM
|
|
}
|
|
},
|
|
async beforeRegisterNodeDef(nodeType, nodeData, app) {
|
|
// NOTE: May need manual adjusting for the few non-namespaced nodes
|
|
if(nodeData?.name?.startsWith("ADE_") && nodeData.description) {
|
|
let description = nodeData.description
|
|
let el = document.createElement("div")
|
|
el.innerHTML = description
|
|
if (!el.children.length) {
|
|
//Is plaintext. Do minor convenience formatting
|
|
let chunks = description.split('\n')
|
|
nodeData.description = chunks[0]
|
|
description = chunks.join('<br>')
|
|
} else {
|
|
nodeData.description = el.querySelector('#VHS_shortdesc')?.innerHTML || el.children[1]?.firstChild?.innerHTML
|
|
}
|
|
chainCallback(nodeType.prototype, "onNodeCreated", function () {
|
|
helpDOM.addHelp(this, nodeType, description)
|
|
})
|
|
}
|
|
},
|
|
});
|