Fix rating picker clicks: use canvas button widget instead of DOM element

addDOMWidget clicks were swallowed by the canvas. Switched to addWidget
button which ComfyUI routes correctly. Picker now positions at mouse coords
from the widget callback event.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
digital-garbage
2026-05-30 11:42:46 +03:00
co-authored by Claude Sonnet 4.6
parent 94d1b86493
commit a4d1f67502
+22 -49
View File
@@ -109,14 +109,6 @@ function injectStyles() {
.fp-picker-opt-hint { font-size: 9px; color: #888; line-height: 1.3; }
.fp-picker-opt:hover .fp-picker-opt-hint { color: #aaa; }
.fp-picker-nuclear { padding: 8px 10px; display: grid; grid-template-columns: 1fr 1fr; gap: 5px; }
.fp-picker-btn-label {
text-align: left; width: 100%; padding: 4px 8px;
background: rgba(255,255,255,0.05);
border: 1px solid rgba(255,255,255,0.12);
border-radius: 5px; color: #bbb; cursor: pointer;
font: 11px sans-serif; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.fp-picker-btn-label:hover { background: rgba(255,255,255,0.09); color: #eee; }
`;
document.head.append(s);
}
@@ -165,7 +157,7 @@ function makeOption(r, currentValue, accent, onPick) {
return btn;
}
function openPicker(ratingWidget, buttonEl, onPick) {
function openPicker(ratingWidget, event, onPick) {
closePicker();
injectStyles();
@@ -213,20 +205,18 @@ function openPicker(ratingWidget, buttonEl, onPick) {
document.body.append(picker);
// Position: below the button, clamped to viewport
const rect = buttonEl.getBoundingClientRect();
// Position at mouse, clamped to viewport
const cx = event?.clientX ?? window.innerWidth / 2;
const cy = event?.clientY ?? window.innerHeight / 2;
requestAnimationFrame(() => {
const ph = picker.offsetHeight, pw = picker.offsetWidth;
let top = rect.bottom + 4, left = rect.left;
if (top + ph > window.innerHeight - 8) top = rect.top - ph - 4;
if (left + pw > window.innerWidth - 8) left = window.innerWidth - pw - 8;
picker.style.top = Math.max(8, top) + "px";
picker.style.left = Math.max(8, left) + "px";
picker.style.left = Math.max(8, Math.min(cx, window.innerWidth - pw - 8)) + "px";
picker.style.top = Math.max(8, Math.min(cy, window.innerHeight - ph - 8)) + "px";
});
// Close on outside click
const onOutside = (e) => {
if (!picker.contains(e.target) && e.target !== buttonEl) {
if (!picker.contains(e.target)) {
closePicker();
document.removeEventListener("mousedown", onOutside, true);
}
@@ -251,7 +241,6 @@ function hideWidget(widget) {
}
function setupRatingPicker(node) {
// Avoid double-setup
if (node.__fpPickerSetup) return;
node.__fpPickerSetup = true;
@@ -260,40 +249,24 @@ function setupRatingPicker(node) {
hideWidget(ratingWidget);
// DOM button that lives outside the canvas
let buttonEl = node.__fpPickerBtn;
if (!buttonEl) {
buttonEl = document.createElement("button");
buttonEl.className = "fp-picker-btn-label";
node.__fpPickerBtn = buttonEl;
}
const updateBtn = (label) => {
const current = label || ratingWidget.value || FORGET_LABEL;
const waiting = current === FORGET_LABEL;
buttonEl.textContent = waiting ? "Waiting for rating..." : `Rating: ${current}`;
buttonEl.style.opacity = waiting ? "0.5" : "1";
const getLabel = () => {
const v = ratingWidget.value;
return (!v || v === FORGET_LABEL) ? "Waiting for rating..." : `Rating: ${v}`;
};
updateBtn(ratingWidget.value);
buttonEl.addEventListener("click", () => {
openPicker(ratingWidget, buttonEl, (label) => {
node.widgets = (node.widgets || []).filter((w) => w.__fpPickerWidget !== true);
const pickerWidget = node.addWidget("button", getLabel(), null, (value, canvas, n, pos, event) => {
openPicker(ratingWidget, event, (label) => {
ratingWidget.value = label;
ratingWidget.callback?.(label);
updateBtn(label);
closePicker();
pickerWidget.name = getLabel();
node.setDirtyCanvas?.(true, true);
});
});
// Attach button below the node using a custom DOM widget
node.widgets = (node.widgets || []).filter((w) => w.__fpPickerWidget !== true);
const domWidget = node.addDOMWidget("fp_rating_picker", "div", buttonEl, {
getValue: () => ratingWidget.value,
setValue: (v) => { ratingWidget.value = v; updateBtn(v); },
serialize: false,
});
if (domWidget) domWidget.__fpPickerWidget = true;
}, { serialize: false });
pickerWidget.__fpPickerWidget = true;
node.__fpPickerWidgetRef = pickerWidget;
node.setDirtyCanvas?.(true, true);
}
@@ -302,9 +275,9 @@ function resetRating(node) {
if (!ratingWidget) return;
ratingWidget.value = FORGET_LABEL;
ratingWidget.callback?.(FORGET_LABEL);
if (node.__fpPickerBtn) {
node.__fpPickerBtn.textContent = "Waiting for rating...";
node.__fpPickerBtn.style.opacity = "0.5";
if (node.__fpPickerWidgetRef) {
node.__fpPickerWidgetRef.name = "Waiting for rating...";
node.setDirtyCanvas?.(true, true);
}
}