diff --git a/web/js/betterCombos.js b/web/js/betterCombos.js index 267f354..15da585 100644 --- a/web/js/betterCombos.js +++ b/web/js/betterCombos.js @@ -5,6 +5,8 @@ import { api } from "../../../scripts/api.js"; const CHECKPOINT_LOADER = "CheckpointLoader|pysssss"; const LORA_LOADER = "LoraLoader|pysssss"; +const IMAGE_WIDTH = 384; +const IMAGE_HEIGHT = 384; function getType(node) { if (node.comfyClass === CHECKPOINT_LOADER) { @@ -13,6 +15,50 @@ function getType(node) { return "loras"; } +const getImage = (imageId) => document.querySelector(`#${CSS.escape(imageId)}`); + +const calculateImagePosition = (el, rootRect, bodyRect) => { + const { x } = el.getBoundingClientRect(); + let { top, left } = rootRect; + const { width: bodyWidth, height: bodyHeight } = bodyRect; + + const isSpaceRight = x + IMAGE_WIDTH <= bodyWidth; + if (isSpaceRight) { + left += rootRect.width + IMAGE_WIDTH; + } + + const isSpaceBelow = rootRect.top + IMAGE_HEIGHT <= bodyHeight; + if (!isSpaceBelow) { + top = bodyHeight - IMAGE_HEIGHT; + } + + return { left: Math.round(left), top: Math.round(top) }; +}; + +function showImage(el, imageId, getRootRect) { + const img = getImage(imageId); + if (img) { + const rootRect = getRootRect(); + if (!rootRect) return; + + const bodyRect = document.body.getBoundingClientRect(); + if (!bodyRect) return; + + const { left, top } = calculateImagePosition(el, rootRect, bodyRect); + + img.style.display = "block"; + img.style.left = `${left}px`; + img.style.top = `${top}px`; + } +} + +function closeImage(imageId) { + const img = getImage(imageId); + if (img) { + img.style.display = "none"; + } +} + app.registerExtension({ name: "pysssss.Combo++", init() { @@ -27,8 +73,8 @@ app.registerExtension({ left: 0; top: 0; transform: translate(-100%, 0); - width: 384px; - height: 384px; + width: ${IMAGE_WIDTH}px; + height: ${IMAGE_HEIGHT}px; background-size: contain; background-position: top right; background-repeat: no-repeat; @@ -81,13 +127,23 @@ app.registerExtension({ // After an element is created for an item, add an image if it has one contextMenuHook["addItem"].push(function (el, menu, [name, value, options]) { if (el && isCustomItem(value) && value?.image && !value.submenu) { + const key = `pysssss-image-combo-${name}`; el.textContent += " *"; - $el("div.pysssss-combo-image", { - parent: el, + $el(`div.pysssss-combo-image`, { + id: key, + parent: document.body, style: { backgroundImage: `url(/pysssss/view/${encodeRFC3986URIComponent(value.image)})`, }, }); + + const getRootRect = () => menu.root?.getBoundingClientRect(); + const showHandler = () => showImage(el, key, getRootRect); + const closeHandler = () => closeImage(key); + + el.addEventListener("mouseenter", showHandler, { passive: true }); + el.addEventListener("mouseleave", closeHandler, { passive: true }); + el.addEventListener("click", closeHandler, { passive: true }); } });