Eji-Sensei14's picture
Upload folder using huggingface_hub
c6535db verified
import { BaseSteppedWidget } from "./BaseSteppedWidget.js"
import { warnDeprecated, clamp } from "../utils/widget.js"
const UNIQUE_MESSAGE_LIMIT = 10_000
/**
* This is used as an (invalid) assertion to resolve issues with legacy duck-typed values.
*
* Function style in use by:
* https://github.com/kijai/ComfyUI-KJNodes/blob/c3dc82108a2a86c17094107ead61d63f8c76200e/web/js/setgetnodes.js#L401-L404
*/
function toArray(values) {
return Array.isArray(values) ? values : Object.keys(values)
}
export class ComboWidget extends BaseSteppedWidget {
constructor(widget, node) {
super(widget, node);
this.type = "combo";
}
get _displayValue() {
const { values: rawValues } = this.options
if (rawValues) {
const values = typeof rawValues === "function" ? rawValues() : rawValues
if (values && !Array.isArray(values)) {
return values[this.value]
}
}
return typeof this.value === "number" ? String(this.value) : this.value
}
#getValues(node) {
const { values } = this.options
if (values == null) throw new Error("[ComboWidget]: values is required")
return typeof values === "function"
? values(this, node)
: values
}
/**
* Checks if the value is {@link Array.at at} the given index in the combo list.
* @param increment `true` if checking the use of the increment button, `false` for decrement
* @returns `true` if the value is at the given index, otherwise `false`.
*/
#canUseButton(increment) {
const { values } = this.options
// If using legacy duck-typed method, false is the most permissive return value
if (typeof values === "function") return false
const valuesArray = toArray(values)
if (!(valuesArray.length > 1)) return false
// Edge case where the value is both the first and last item in the list
const firstValue = valuesArray.at(0)
const lastValue = valuesArray.at(-1)
if (firstValue === lastValue) return true
return this.value !== (increment ? lastValue : firstValue)
}
/**
* Returns `true` if the current value is not the last value in the list.
* Handles edge case where the value is both the first and last item in the list.
*/
canIncrement() {
return this.#canUseButton(true)
}
canDecrement() {
return this.#canUseButton(false)
}
incrementValue(options) {
this.#tryChangeValue(1, options)
}
decrementValue(options) {
this.#tryChangeValue(-1, options)
}
#tryChangeValue(delta, options) {
const values = this.#getValues(options.node)
const indexedValues = toArray(values)
// avoids double click event
options.canvas.last_mouseclick = 0
const foundIndex = typeof values === "object"
? indexedValues.indexOf(String(this.value)) + delta
: indexedValues.indexOf(this.value) + delta
const index = clamp(foundIndex, 0, indexedValues.length - 1)
const value = Array.isArray(values)
? values[index]
: index
this.setValue(value, options)
}
onClick({ e, node, canvas }) {
const x = e.canvasX - node.pos[0]
const width = this.width || node.size[0]
// Deprecated functionality (warning as of v0.14.5)
if (typeof this.options.values === "function") {
warnDeprecated("Using a function for values is deprecated. Use an array of unique values instead.")
}
// Determine if clicked on left/right arrows
if (x < 40) return this.decrementValue({ e, node, canvas })
if (x > width - 40) return this.incrementValue({ e, node, canvas })
// Otherwise, show dropdown menu
const values = this.#getValues(node)
const values_list = toArray(values)
// Handle center click - show dropdown menu
const text_values = values != values_list ? Object.values(values) : values
new LiteGraph.ContextMenu(text_values, {
scale: Math.max(1, canvas.ds.scale),
event: e,
className: "dark",
callback: (value) => {
this.setValue(
values != values_list
? text_values.indexOf(value)
: value,
{ e, node, canvas },
)
},
})
}
}