From ecaf57e40514b6ed724479b1778c617a19518aac Mon Sep 17 00:00:00 2001 From: bird_d Date: Sun, 3 Sep 2023 17:23:33 -0400 Subject: [PATCH 01/13] Image Feed: Overhaul - Makes the panel resizable using a bar on the open side - Slight styling changes - Removed column/image feed size options - Added image size option - Grid should auto-adjust based on the size and desired image size - Holding Ctrl while resizing will snap to the current image size * step - Changed the "Resize Feed" setting text to a Cog emoji - Use the .div for image containers instead of .img to adjust brightness - Moved css variables to the new image-feed-root - Always show the inner feed box --- web/js/imageFeed.js | 291 ++++++++++++++++++++++++++++++++------------ 1 file changed, 211 insertions(+), 80 deletions(-) diff --git a/web/js/imageFeed.js b/web/js/imageFeed.js index 0d1f340..e3e3399 100644 --- a/web/js/imageFeed.js +++ b/web/js/imageFeed.js @@ -5,44 +5,68 @@ import { lightbox } from "./common/lightbox.js"; $el("style", { textContent: ` - .pysssss-image-feed { + .pysssss-image-feed-root { + --image-size: 128px; + --image-gap-size: 4px; + + --image-feed-radius: 5px; + --image-feed-theme-handle: #333; + + + align-items: stretch; + display: flex; + font-size: 12px; position: absolute; - background: var(--comfy-menu-bg); - color: var(--fg-color); + text-align: center; + vertical-align: top; z-index: 99; - font-family: sans-serif; - font-size: 12px; - display: flex; - flex-direction: column; } - .pysssss-image-feed--top, .pysssss-image-feed--bottom { + .pysssss-image-feed-root--left, .pysssss-image-feed-root--right { + top: 0; + height: 100vh; + width: calc(var(--image-size) + 76px); + } + .pysssss-image-feed-root--left { + left: 0; + } + .pysssss-image-feed-root--right { + right: 0; + flex-direction: row-reverse; + } + .pysssss-image-feed-root--top, .pysssss-image-feed-root--bottom { + left: 0; width: 100vw; - min-height: 30px; - max-height: calc(var(--max-size, 20) * 1vh); + height: calc(var(--image-size) + 76px); } - .pysssss-image-feed--top { + .pysssss-image-feed-root--top { top: 0; + flex-direction: column; } - .pysssss-image-feed--bottom { + .pysssss-image-feed-root--bottom { bottom: 0; flex-direction: column-reverse; - padding-top: 5px; + } + .pysssss-image-feed { + flex: 1; + background: var(--comfy-menu-bg); + color: var(--fg-color); + font-family: sans-serif; + font-size: 12px; + display: flex; + flex-direction: column; + width: 100%; + height: 100%; } .pysssss-image-feed--left, .pysssss-image-feed--right { top: 0; height: 100vh; - min-width: 200px; - max-width: calc(var(--max-size, 10) * 1vw); - } - .pysssss-image-feed--left { - left: 0; - } - .pysssss-image-feed--right { - right: 0; + width: 100%; } - .pysssss-image-feed--left .pysssss-image-feed-menu, .pysssss-image-feed--right .pysssss-image-feed-menu { - flex-direction: column; + .pysssss-image-feed--top, .pysssss-image-feed--bottom { + left: 0; + width: 100vw; + height: 100%; } .pysssss-image-feed-menu { @@ -62,7 +86,7 @@ $el("style", { } .pysssss-image-feed-btn { background-color:var(--comfy-input-bg); - border-radius:5px; + border-radius: var(--image-feed-radius); border:2px solid var(--border-color); color: var(--fg-color); cursor:pointer; @@ -87,9 +111,8 @@ $el("style", { position:relative; top:1px; } - .pysssss-image-feed-menu section { - border-radius: 5px; + border-radius: var(--image-feed-radius); background: rgba(0,0,0,0.6); padding: 0 5px; display: flex; @@ -103,7 +126,7 @@ $el("style", { .pysssss-image-feed-menu section input { flex: 1 1 100%; background: rgba(0,0,0,0.6); - border-radius: 5px; + border-radius: var(--image-feed-radius); overflow: hidden; z-index: 100; } @@ -122,6 +145,7 @@ $el("style", { } .sizing-menu:hover .size-controls-flyout { + position: absolute; transform: scale(1, 1); transition: 200ms linear; transition-delay: 0; @@ -150,57 +174,86 @@ $el("style", { top: 0; right: 0; } - + .pysssss-image-feed-menu > * { min-height: 24px; } .pysssss-image-feed-list { - flex: 1 1 auto; + align-content: flex-start; overflow-y: auto; display: grid; + gap: var(--image-gap-size); + grid-template-columns: repeat( auto-fit, minmax(var(--image-size, 128), 1fr) ); align-items: center; - justify-content: center; - gap: 4px; - grid-auto-rows: min-content; - grid-template-columns: repeat(var(--img-sz, 3), 1fr); + justify-items: center; transition: 100ms linear; scrollbar-gutter: stable both-edges; padding: 5px; background: var(--comfy-input-bg); - border-radius: 5px; + border-radius: var(--image-feed-radius); margin: 5px; margin-top: 0px; + height: 100%; } - .pysssss-image-feed-list:empty { - display: none; + .pysssss-image-feed-list div, .pysssss-image-feed-list a { + display: flex; + flex-wrap: wrap; + width: var(--image-size); + height: var(--image-size); + align-content: center; + justify-content: center; } .pysssss-image-feed-list div { - height: 100%; - text-align: center; + background: rgba(0,0,0,0.15); + box-sizing: border-box; + } + .pysssss-image-feed-list div:hover{ + filter: brightness(1.2); + } + .pysssss-image-feed-list img { + max-width: 100%; + max-height: 100%; } .pysssss-image-feed-list::-webkit-scrollbar { background: var(--comfy-input-bg); - border-radius: 5px; + border-radius: var(--image-feed-radius); } .pysssss-image-feed-list::-webkit-scrollbar-thumb { background:var(--comfy-menu-bg); border: 5px solid transparent; - border-radius: 8px; + border-radius: var(--image-feed-radius); background-clip: content-box; } .pysssss-image-feed-list::-webkit-scrollbar-thumb:hover { background: var(--border-color); background-clip: content-box; } - .pysssss-image-feed-list img { - object-fit: var(--img-fit, contain); - max-width: 100%; - max-height: calc(var(--max-size) * 1vh); - border-radius: 4px; + .pysssss-image-feed-handle { + flex: 0; + background-color: #191919; + display: flex; + align-items: center; + justify-content: center; + + /* Disables dragging so it doesn't mess up the grab. */ + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + } + .pysssss-image-feed-handle--left, .pysssss-image-feed-handle--right { + cursor: col-resize; + min-width: 8px; + height: 100vh; } - .pysssss-image-feed-list img:hover { - filter: brightness(1.2); - }`, + .pysssss-image-feed-handle--top, .pysssss-image-feed-handle--bottom { + cursor: row-resize; + min-height: 8px; + width: 100vw; + } + .pysssss-image-feed-handle:hover, .pysssss-image-feed-handle:active { + background-color: var(--border-color); + } + `, parent: document.body, }); @@ -229,11 +282,18 @@ app.registerExtension({ localStorage.setItem("pysssss.ImageFeed." + n, v); }; - const imageFeed = $el("div.pysssss-image-feed", { + const imageFeedRoot = $el("div.pysssss-image-feed-root", { parent: document.body, }); + + const imageFeed = $el("div.pysssss-image-feed"); + const imageList = $el("div.pysssss-image-feed-list"); + const resizeHandle = $el("div.pysssss-image-feed-handle"); + + imageFeedRoot.append(imageFeed, resizeHandle); + const feedLocation = app.ui.settings.addSetting({ id: "pysssss.ImageFeed.Location", name: "🐍 Image Feed Location", @@ -254,7 +314,7 @@ app.registerExtension({ }, oninput: (e) => { feedLocation.value = e.target.value; - imageFeed.className = `pysssss-image-feed pysssss-image-feed--${feedLocation.value}`; + onFeedLocationChange(feedLocation.value); }, }, ["left", "top", "right", "bottom"].map((m) => @@ -269,10 +329,23 @@ app.registerExtension({ ]); }, onChange(value) { - imageFeed.className = `pysssss-image-feed pysssss-image-feed--${value}`; + onFeedLocationChange(value); }, }); + function onFeedLocationChange(value) { + imageFeed.className = `pysssss-image-feed pysssss-image-feed--${value}`; + imageFeedRoot.className = `pysssss-image-feed-root pysssss-image-feed-root--${value}`; + resizeHandle.className = `pysssss-image-feed-handle pysssss-image-feed-handle--${value}`; + if (["left", "right"].includes(value)) { + imageFeedRoot.style.width = "max(calc(var(--image-size) + 32px), 200px)"; + imageFeedRoot.style.height = "unset"; + } else { + imageFeedRoot.style.height = "max(calc(var(--image-size) + 32px), 200px)"; + imageFeedRoot.style.width = "unset"; + } + } + const feedDirection = app.ui.settings.addSetting({ id: "pysssss.ImageFeed.Direction", name: "🐍 Image Feed Direction", @@ -317,7 +390,7 @@ app.registerExtension({ const hideButton = $el("button.pysssss-image-feed-btn.hide-btn", { textContent: "❌", onclick: () => { - imageFeed.style.display = "none"; + imageFeedRoot.style.display = "none"; showButton.style.display = "unset"; saveVal("Visible", 0); visible = false; @@ -327,44 +400,26 @@ app.registerExtension({ imageFeed.append( $el("div.pysssss-image-feed-menu", [ $el("section.sizing-menu", {}, [ - $el("label.size-control-handle", { textContent: "↹ Resize Feed" }), + $el("label.size-control-handle", { textContent: "⚙️" }), $el("div.size-controls-flyout", {}, [ $el("section.size-control.feed-size-control", {}, [ $el("span", { - textContent: "Feed Size...", - }), - $el("input", { - type: "range", - min: 10, - max: 80, - oninput: (e) => { - e.target.parentElement.title = `Controls the maximum size of the image feed panel (${e.target.value}vh)`; - imageFeed.style.setProperty("--max-size", e.target.value); - saveVal("FeedSize", e.target.value); - }, - $: (el) => { - requestAnimationFrame(() => { - el.value = getVal("FeedSize", 25); - el.oninput({ target: el }); - }); - }, + textContent: "Image Size", }), - ]), - $el("section.size-control.image-size-control", {}, [ - $el("span", { textContent: "Column count..." }), $el("input", { type: "range", - min: 1, - max: 10, - step: 1, + min: 32, + max: 512, + step: 12, oninput: (e) => { - e.target.parentElement.title = `Controls the number of columns in the feed (${e.target.value} columns)`; - imageFeed.style.setProperty("--img-sz", e.target.value); + e.target.parentElement.title = `Controls the maximum size of the images in the feed panel (${e.target.value}px/512px)`; + imageFeedRoot.style.setProperty("--image-size", `${e.target.value}px` ); saveVal("ImageSize", e.target.value); + e.target.textContent = `${e.target.value}px/512px`; }, $: (el) => { requestAnimationFrame(() => { - el.value = getVal("ImageSize", 4); + el.value = getVal("ImageSize", 128); el.oninput({ target: el }); }); }, @@ -377,7 +432,7 @@ app.registerExtension({ imageList ); showButton.onclick = () => { - imageFeed.style.display = "block"; + imageFeedRoot.style.display = "flex"; showButton.style.display = "none"; saveVal("Visible", 1); visible = true; @@ -402,6 +457,7 @@ app.registerExtension({ "a", { target: "_blank", + draggable: false, href, onclick: (e) => { const imgs = [...imageList.querySelectorAll("img")].map((img) => img.getAttribute("src")); @@ -416,5 +472,80 @@ app.registerExtension({ } } }); + + let isResizing = false; + let initialSize = 0; + let imageFeedRootSize = 0; + let paddingSize = 0; + let isVertical = false; + + resizeHandle.addEventListener('mousedown', (e) => { + isResizing = true; + isVertical = ["left", "right"].includes(feedLocation.value); + + if (isVertical) { + initialSize = e.clientX; + imageFeedRootSize = parseFloat(getComputedStyle(imageFeedRoot).width); + paddingSize = (imageFeedRootSize - parseFloat(getComputedStyle(imageFeed).width)) * 8; + } else { + initialSize = e.clientY; + imageFeedRootSize = parseFloat(getComputedStyle(imageFeedRoot).height); + paddingSize = (imageFeedRootSize - parseFloat(getComputedStyle(imageFeed).height)) * 16; + } + + + [...imageList.querySelectorAll("img")].map((img) => { + img.setAttribute("draggable", false); + }); + + document.addEventListener('mousemove', resizeContainer); + document.addEventListener('mouseup', stopResize); + }); + + function resizeContainer(e) { + if (!isResizing) { + return; + } + + var newSize = 0; + + if (isVertical) { + const deltaX = e.clientX - initialSize; + newSize = feedLocation.value == "left" ? imageFeedRootSize + deltaX : imageFeedRootSize - deltaX; + } else { + const deltaY = e.clientY - initialSize; + newSize = feedLocation.value == "top" ? imageFeedRootSize + deltaY : imageFeedRootSize - deltaY; + } + + const minSize = getVal("ImageSize", 128); + const maxSize = isVertical ? window.innerWidth : window.innerHeight; + + /* Snap to closest image-size multiple for a cleaner list */ + if (e.ctrlKey) { + let steps = Math.round(newSize / minSize) + let gapSize = parseFloat(getComputedStyle(imageFeedRoot).getPropertyValue("--image-gap-size")); + newSize = steps * minSize; + newSize = newSize + paddingSize + (steps * (gapSize + 2)); + } + + if (newSize >= minSize && newSize <= maxSize) { + if (isVertical) { + imageFeedRoot.style.width = `max(calc(var(--image-size) + 76px), ${newSize}px, 200px)`; + + } else { + imageFeedRoot.style.height = `max(calc(var(--image-size) + 76px), ${newSize}px, 200px)`; + } + } + } + + function stopResize() { + isResizing = false; + document.removeEventListener('mousemove', resizeContainer); + document.removeEventListener('mouseup', stopResize); + + [...imageList.querySelectorAll("img")].map((img) => { + img.setAttribute("draggable", true); + }); + } }, }); From 72648d1c0f5948b7caab0b1856de75b8a3eb7bb4 Mon Sep 17 00:00:00 2001 From: bird_d Date: Sun, 3 Sep 2023 22:42:53 -0400 Subject: [PATCH 02/13] Image Feed: Resizing panel + Shift resizes images --- web/js/imageFeed.js | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/web/js/imageFeed.js b/web/js/imageFeed.js index e3e3399..e0efc86 100644 --- a/web/js/imageFeed.js +++ b/web/js/imageFeed.js @@ -493,7 +493,6 @@ app.registerExtension({ paddingSize = (imageFeedRootSize - parseFloat(getComputedStyle(imageFeed).height)) * 16; } - [...imageList.querySelectorAll("img")].map((img) => { img.setAttribute("draggable", false); }); @@ -508,7 +507,6 @@ app.registerExtension({ } var newSize = 0; - if (isVertical) { const deltaX = e.clientX - initialSize; newSize = feedLocation.value == "left" ? imageFeedRootSize + deltaX : imageFeedRootSize - deltaX; @@ -517,9 +515,20 @@ app.registerExtension({ newSize = feedLocation.value == "top" ? imageFeedRootSize + deltaY : imageFeedRootSize - deltaY; } + /* Change image size */ + if (e.shiftKey && !e.ctrlKey) { + let newImageSize = parseInt(getVal("ImageSize")) + (isVertical ? e.movementX : e.movementY); + newImageSize = Math.min(Math.max(32, newImageSize), 512); + + saveVal("ImageSize", newImageSize); + imageFeedRoot.style.setProperty("--image-size", `${newImageSize}px`); + /* Update settings slider */ + document.querySelector("section.size-control.feed-size-control input").value = newImageSize; + } + const minSize = getVal("ImageSize", 128); const maxSize = isVertical ? window.innerWidth : window.innerHeight; - + /* Snap to closest image-size multiple for a cleaner list */ if (e.ctrlKey) { let steps = Math.round(newSize / minSize) @@ -531,7 +540,6 @@ app.registerExtension({ if (newSize >= minSize && newSize <= maxSize) { if (isVertical) { imageFeedRoot.style.width = `max(calc(var(--image-size) + 76px), ${newSize}px, 200px)`; - } else { imageFeedRoot.style.height = `max(calc(var(--image-size) + 76px), ${newSize}px, 200px)`; } From ad898db1c978b0d35b8c74edb6f92002b35e54da Mon Sep 17 00:00:00 2001 From: bird_d Date: Sun, 3 Sep 2023 23:09:06 -0400 Subject: [PATCH 03/13] Image Feed: Fix flickering cursor Fixes the flickering for when the mouse is too fast for the handle --- web/js/imageFeed.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/web/js/imageFeed.js b/web/js/imageFeed.js index e0efc86..8c1f565 100644 --- a/web/js/imageFeed.js +++ b/web/js/imageFeed.js @@ -497,6 +497,8 @@ app.registerExtension({ img.setAttribute("draggable", false); }); + document.documentElement.style.cursor = isVertical ? "col-resize" : "row-resize"; + document.addEventListener('mousemove', resizeContainer); document.addEventListener('mouseup', stopResize); }); @@ -551,6 +553,8 @@ app.registerExtension({ document.removeEventListener('mousemove', resizeContainer); document.removeEventListener('mouseup', stopResize); + document.documentElement.style.cursor = "default"; + [...imageList.querySelectorAll("img")].map((img) => { img.setAttribute("draggable", true); }); From 7e115c0d12b883f8bed44aea0a75581a18a314e8 Mon Sep 17 00:00:00 2001 From: bird_d Date: Fri, 8 Sep 2023 19:03:17 -0400 Subject: [PATCH 04/13] Initial popup implementation See image feed for an example of usage. --- web/js/common/popup.css | 40 ++++++++++++++ web/js/common/popup.js | 119 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 159 insertions(+) create mode 100644 web/js/common/popup.css create mode 100644 web/js/common/popup.js diff --git a/web/js/common/popup.css b/web/js/common/popup.css new file mode 100644 index 0000000..b0777bb --- /dev/null +++ b/web/js/common/popup.css @@ -0,0 +1,40 @@ +.pysssss-popup { + /* Could be globals somewhere else? */ + --popup-radius: 5px; + + position: absolute; + left: 0px; + top: 0px; + font-family: sans-serif; + font-size: 12px; + z-index: 199; + border-radius: var(--popup-radius); + padding: 5px; + + width: 100%; + height: 100%; + + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + /* pointer-events: none; */ +} + +.pysssss-popup-header { + border-bottom: 1px solid var(--border-color); + text-align: right; + padding: 4px; +} + +.pysssss-popup-container { + position: absolute; + left: 0px; + top: 0px; + clear: both; + background: #191919cc; + /* border: 2px solid var(--border-color); */ + border-radius: var(--popup-radius); + width: auto; + height: auto; + padding: 4px; +} \ No newline at end of file diff --git a/web/js/common/popup.js b/web/js/common/popup.js new file mode 100644 index 0000000..1974d57 --- /dev/null +++ b/web/js/common/popup.js @@ -0,0 +1,119 @@ +import { $el, ComfyDialog } from "../../../../scripts/ui.js"; +import { api } from "../../../../scripts/api.js"; +import { addStylesheet } from "./utils.js"; + +addStylesheet(import.meta.url); + +export class PopUp { + constructor(element, options) { + let name = options.name; + this.activeOnHover = options.activeOnHover; + + this.root = $el(`div.pysssss-popup`, { + parent: document.body, + }); + this.container = $el("div.pysssss-popup-container"); + + if (name) { + const header = $el("div.pysssss-popup-header"); + const headerLabel = $el("div.pysssss-popup-label") + header.append(headerLabel); + headerLabel.innerHTML = name; + + this.container.append(header); + } + + this.content = $el("div.pysssss-popup-content"); + + this.root.append(this.container); + this.container.append(this.content); + + + this.onContainerLeaveBind = this.onContainerLeave.bind(this); + this.onRootClickBind = this.onRootClick.bind(this); + this.onShowBind = this.show.bind(this); + // this.onHideBind = this.hide.bind(this); + + this.attach(element); + this.hide(); + } + + /* (content) => {} */ + setContent(contentCallback) { + contentCallback(this.content); + } + + hide() { + this.root.style.display = "none"; + + this.container.removeEventListener('mouseleave', this.onContainerLeaveBind); + this.root.removeEventListener('mousedown', this.onRootClickBind); + } + + show() { + this.root.style.display = "flex"; + + this.validatePosition(); + + this.container.addEventListener('mouseleave', this.onContainerLeaveBind); + this.root.addEventListener('mousedown', this.onRootClickBind); + } + + attach(element) { + this.attachedElement = element; + if (this.activeOnHover) { + element.addEventListener('mouseenter', this.onShowBind); + } else { + element.addEventListener('mousedown', this.onShowBind); + } + } + + detach() { + let element = this.attachedElement; + if (this.activeOnHover) { + element.removeEventListener('mouseenter', this.onShowBind); + } else { + element.removeEventListener('mousedown', this.onShowBind); + } + + this.attachedElement = null; + } + + onContainerLeave(event) { + this.hide(); + } + + onRootClick(event) { + if (this.container.matches(":hover")) { + return; + } + + this.hide(); + } + + validatePosition() { + /* Set to position of attached element*/ + let rect = this.attachedElement.getBoundingClientRect(); + let computedStyle = getComputedStyle(this.container); + + let windowWidth = window.innerWidth; + let windowHeight = window.innerHeight; + let containerWidth = parseFloat(computedStyle.width); + let containerHeight = parseFloat(computedStyle.height); + + let x = rect.left; + let y = rect.top; + + this.container.style.left = `${x}px`; + this.container.style.top = `${y - (rect.top - rect.bottom) + 4}px`; + + /* Make sure popup is fully inside window */ + if (x + containerWidth > windowWidth) { + this.container.style.left = `${windowWidth - containerWidth * 2.0 - (rect.left - rect.right) * 2.0}px`; + } + + if (y + containerHeight > windowHeight) { + this.container.style.top = `${windowHeight - containerHeight * 2.0 - 4}px`; + } + } +} \ No newline at end of file From 860a2ecab79ebd12cda12e7ca8e46cc2aa4372f1 Mon Sep 17 00:00:00 2001 From: bird_d Date: Fri, 8 Sep 2023 19:38:44 -0400 Subject: [PATCH 05/13] Popup: Account for btn height Popup shouldn't go off the bottom screen now --- web/js/common/popup.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/web/js/common/popup.js b/web/js/common/popup.js index 1974d57..20dcbd6 100644 --- a/web/js/common/popup.js +++ b/web/js/common/popup.js @@ -96,6 +96,8 @@ export class PopUp { let rect = this.attachedElement.getBoundingClientRect(); let computedStyle = getComputedStyle(this.container); + let buttonWidth = rect.right - rect.left; + let buttonHeight = rect.top - rect.bottom; let windowWidth = window.innerWidth; let windowHeight = window.innerHeight; let containerWidth = parseFloat(computedStyle.width); @@ -105,15 +107,15 @@ export class PopUp { let y = rect.top; this.container.style.left = `${x}px`; - this.container.style.top = `${y - (rect.top - rect.bottom) + 4}px`; + this.container.style.top = `${y - buttonHeight + 2}px`; /* Make sure popup is fully inside window */ if (x + containerWidth > windowWidth) { - this.container.style.left = `${windowWidth - containerWidth * 2.0 - (rect.left - rect.right) * 2.0}px`; + this.container.style.left = `${windowWidth - containerWidth * 2.0 + buttonWidth}px`; } - if (y + containerHeight > windowHeight) { - this.container.style.top = `${windowHeight - containerHeight * 2.0 - 4}px`; + if (y + containerHeight * 2.0 + (buttonHeight) > windowHeight) { + this.container.style.top = `${windowHeight - containerHeight * 2.0 + buttonHeight / 2.0 - 2.0}px`; } } } \ No newline at end of file From 0dc2b74075a5b4dadbdb48a6631ac52e00c7d5b3 Mon Sep 17 00:00:00 2001 From: bird_d Date: Fri, 8 Sep 2023 19:43:28 -0400 Subject: [PATCH 06/13] Image Feed: Use popup widget for settings --- web/js/imageFeed.js | 66 ++++++++++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 28 deletions(-) diff --git a/web/js/imageFeed.js b/web/js/imageFeed.js index 8c1f565..6614ae6 100644 --- a/web/js/imageFeed.js +++ b/web/js/imageFeed.js @@ -2,6 +2,7 @@ import { api } from "../../../scripts/api.js"; import { app } from "../../../scripts/app.js"; import { $el } from "../../../scripts/ui.js"; import { lightbox } from "./common/lightbox.js"; +import { PopUp } from "./common/popup.js"; $el("style", { textContent: ` @@ -397,36 +398,45 @@ app.registerExtension({ }, }); + const settingsButton = $el("button.pysssss-image-feed-btn.settings-btn", {}, [ + $el("label.size-control-handle", { textContent: "⚙️" }) + ]); + + const settingsPopup = new PopUp(settingsButton, { + name: "Feed Settings", + activeOnHover: true, + }); + settingsPopup.setContent( (content) => { + content.append( + $el("section.size-control.feed-size-control", {}, [ + $el("span", { + textContent: "Image Size", + }), + $el("input", { + type: "range", + min: 32, + max: 512, + step: 12, + oninput: (e) => { + e.target.parentElement.title = `Controls the maximum size of the images in the feed panel (${e.target.value}px/512px)`; + imageFeedRoot.style.setProperty("--image-size", `${e.target.value}px` ); + saveVal("ImageSize", e.target.value); + e.target.textContent = `${e.target.value}px/512px`; + }, + $: (el) => { + requestAnimationFrame(() => { + el.value = getVal("ImageSize", 128); + el.oninput({ target: el }); + }); + }, + }), + ]), + ) + }); + imageFeed.append( $el("div.pysssss-image-feed-menu", [ - $el("section.sizing-menu", {}, [ - $el("label.size-control-handle", { textContent: "⚙️" }), - $el("div.size-controls-flyout", {}, [ - $el("section.size-control.feed-size-control", {}, [ - $el("span", { - textContent: "Image Size", - }), - $el("input", { - type: "range", - min: 32, - max: 512, - step: 12, - oninput: (e) => { - e.target.parentElement.title = `Controls the maximum size of the images in the feed panel (${e.target.value}px/512px)`; - imageFeedRoot.style.setProperty("--image-size", `${e.target.value}px` ); - saveVal("ImageSize", e.target.value); - e.target.textContent = `${e.target.value}px/512px`; - }, - $: (el) => { - requestAnimationFrame(() => { - el.value = getVal("ImageSize", 128); - el.oninput({ target: el }); - }); - }, - }), - ]), - ]), - ]), + settingsButton, $el("div.pysssss-image-feed-btn-group", {}, [clearButton, hideButton]), ]), imageList From 0503ef0817bd310a59742bd8ef48a9f97bcb6f1a Mon Sep 17 00:00:00 2001 From: bird_d Date: Wed, 13 Sep 2023 22:57:19 -0400 Subject: [PATCH 07/13] Image Feed: Better css logic Switch from width to min-width/height for the panel size Makes it so when the feed is empty, the width/height is more than any content inside So the header buttons should be the new minimum width for the panel When there are images, the panel will expand to desired image size --- web/js/imageFeed.js | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/web/js/imageFeed.js b/web/js/imageFeed.js index 6614ae6..e2f2652 100644 --- a/web/js/imageFeed.js +++ b/web/js/imageFeed.js @@ -25,7 +25,6 @@ $el("style", { .pysssss-image-feed-root--left, .pysssss-image-feed-root--right { top: 0; height: 100vh; - width: calc(var(--image-size) + 76px); } .pysssss-image-feed-root--left { left: 0; @@ -37,7 +36,6 @@ $el("style", { .pysssss-image-feed-root--top, .pysssss-image-feed-root--bottom { left: 0; width: 100vw; - height: calc(var(--image-size) + 76px); } .pysssss-image-feed-root--top { top: 0; @@ -184,7 +182,6 @@ $el("style", { overflow-y: auto; display: grid; gap: var(--image-gap-size); - grid-template-columns: repeat( auto-fit, minmax(var(--image-size, 128), 1fr) ); align-items: center; justify-items: center; transition: 100ms linear; @@ -339,10 +336,8 @@ app.registerExtension({ imageFeedRoot.className = `pysssss-image-feed-root pysssss-image-feed-root--${value}`; resizeHandle.className = `pysssss-image-feed-handle pysssss-image-feed-handle--${value}`; if (["left", "right"].includes(value)) { - imageFeedRoot.style.width = "max(calc(var(--image-size) + 32px), 200px)"; imageFeedRoot.style.height = "unset"; } else { - imageFeedRoot.style.height = "max(calc(var(--image-size) + 32px), 200px)"; imageFeedRoot.style.width = "unset"; } } @@ -385,7 +380,10 @@ app.registerExtension({ const clearButton = $el("button.pysssss-image-feed-btn.clear-btn", { textContent: "Clear", - onclick: () => imageList.replaceChildren(), + onclick: () => { + imageList.replaceChildren(); + imageList.style.gridTemplateColumns = "unset"; + }, }); const hideButton = $el("button.pysssss-image-feed-btn.hide-btn", { @@ -480,6 +478,9 @@ app.registerExtension({ ]) ); } + if (imageList.childNodes.length > 0) { + imageList.style.gridTemplateColumns = "repeat( auto-fit, minmax(var(--image-size, 128), 1fr) )"; + } } }); @@ -538,22 +539,20 @@ app.registerExtension({ document.querySelector("section.size-control.feed-size-control input").value = newImageSize; } - const minSize = getVal("ImageSize", 128); - const maxSize = isVertical ? window.innerWidth : window.innerHeight; - /* Snap to closest image-size multiple for a cleaner list */ - if (e.ctrlKey) { - let steps = Math.round(newSize / minSize) + if (e.ctrlKey) { + const imageSize = getVal("ImageSize", 128); + let steps = Math.round(newSize / imageSize) let gapSize = parseFloat(getComputedStyle(imageFeedRoot).getPropertyValue("--image-gap-size")); - newSize = steps * minSize; + newSize = steps * imageSize; newSize = newSize + paddingSize + (steps * (gapSize + 2)); } - - if (newSize >= minSize && newSize <= maxSize) { + + if (newSize >= 0.0 && newSize <= isVertical ? window.innerWidth : window.innerHeight) { if (isVertical) { - imageFeedRoot.style.width = `max(calc(var(--image-size) + 76px), ${newSize}px, 200px)`; + imageFeedRoot.style.minWidth = `${newSize}px`; } else { - imageFeedRoot.style.height = `max(calc(var(--image-size) + 76px), ${newSize}px, 200px)`; + imageFeedRoot.style.minHeight = `${newSize}px`; } } } From 5970a020675af465118fd16e6e80e38db06a29a1 Mon Sep 17 00:00:00 2001 From: bird_d Date: Thu, 14 Sep 2023 02:00:02 -0400 Subject: [PATCH 08/13] Popup: Fix slight papercuts Closes when mouse distance is too far Adds an option to set the max distance, defaulting to 50 Makes the container more opaque Fixes outside of window logic --- web/js/common/popup.css | 4 +--- web/js/common/popup.js | 37 +++++++++++++++++++++++++++++-------- 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/web/js/common/popup.css b/web/js/common/popup.css index b0777bb..8270c5d 100644 --- a/web/js/common/popup.css +++ b/web/js/common/popup.css @@ -17,7 +17,6 @@ -moz-user-select: none; -ms-user-select: none; user-select: none; - /* pointer-events: none; */ } .pysssss-popup-header { @@ -31,8 +30,7 @@ left: 0px; top: 0px; clear: both; - background: #191919cc; - /* border: 2px solid var(--border-color); */ + background: #191919fa; border-radius: var(--popup-radius); width: auto; height: auto; diff --git a/web/js/common/popup.js b/web/js/common/popup.js index 20dcbd6..c38a6bc 100644 --- a/web/js/common/popup.js +++ b/web/js/common/popup.js @@ -8,6 +8,7 @@ export class PopUp { constructor(element, options) { let name = options.name; this.activeOnHover = options.activeOnHover; + this.maxMouseDistance = options.maxMouseDistance ? options.maxMouseDistance : 50; this.root = $el(`div.pysssss-popup`, { parent: document.body, @@ -29,10 +30,9 @@ export class PopUp { this.container.append(this.content); - this.onContainerLeaveBind = this.onContainerLeave.bind(this); + this.onRootMouseMoveBind = this.onRootMouseMove.bind(this); this.onRootClickBind = this.onRootClick.bind(this); this.onShowBind = this.show.bind(this); - // this.onHideBind = this.hide.bind(this); this.attach(element); this.hide(); @@ -46,7 +46,7 @@ export class PopUp { hide() { this.root.style.display = "none"; - this.container.removeEventListener('mouseleave', this.onContainerLeaveBind); + this.root.removeEventListener('mousemove', this.onRootMouseMoveBind); this.root.removeEventListener('mousedown', this.onRootClickBind); } @@ -55,7 +55,7 @@ export class PopUp { this.validatePosition(); - this.container.addEventListener('mouseleave', this.onContainerLeaveBind); + this.root.addEventListener('mousemove', this.onRootMouseMoveBind); this.root.addEventListener('mousedown', this.onRootClickBind); } @@ -79,8 +79,29 @@ export class PopUp { this.attachedElement = null; } - onContainerLeave(event) { - this.hide(); + inAttachedElement(event, x, y) { + let rect = this.attachedElement.getBoundingClientRect(); + return rect.left <= x && x <= rect.right && + rect.top <= y && y <= rect.bottom; + } + + onRootMouseMove(event) { + let mouseX = event.x; + let mouseY = event.y; + + if (this.inAttachedElement(event, mouseX, mouseY)) { + return; + } + + let rect = this.container.getBoundingClientRect(); + + /* If certain distance away from popup */ + const maxDistance = 50; + if (rect.top - mouseY > this.maxMouseDistance || mouseY - rect.bottom > this.maxMouseDistance || + rect.left - mouseX > this.maxMouseDistance || mouseX - rect.right > this.maxMouseDistance + ) { + this.hide(); + } } onRootClick(event) { @@ -111,11 +132,11 @@ export class PopUp { /* Make sure popup is fully inside window */ if (x + containerWidth > windowWidth) { - this.container.style.left = `${windowWidth - containerWidth * 2.0 + buttonWidth}px`; + this.container.style.left = `${rect.right - containerWidth - 10.0}px`; } if (y + containerHeight * 2.0 + (buttonHeight) > windowHeight) { - this.container.style.top = `${windowHeight - containerHeight * 2.0 + buttonHeight / 2.0 - 2.0}px`; + this.container.style.top = `${rect.top - containerHeight - 10.0}px`; } } } \ No newline at end of file From 11e8d7a8062986978cba8db2452c142614633d70 Mon Sep 17 00:00:00 2001 From: bird_d Date: Thu, 14 Sep 2023 06:36:10 -0400 Subject: [PATCH 09/13] Image Feed: Move settings to panel --- web/js/imageFeed.js | 206 +++++++++++++++++++++----------------------- 1 file changed, 100 insertions(+), 106 deletions(-) diff --git a/web/js/imageFeed.js b/web/js/imageFeed.js index e2f2652..68fbaa0 100644 --- a/web/js/imageFeed.js +++ b/web/js/imageFeed.js @@ -110,25 +110,6 @@ $el("style", { position:relative; top:1px; } - .pysssss-image-feed-menu section { - border-radius: var(--image-feed-radius); - background: rgba(0,0,0,0.6); - padding: 0 5px; - display: flex; - gap: 5px; - align-items: center; - position: relative; - } - .pysssss-image-feed-menu section span { - white-space: nowrap; - } - .pysssss-image-feed-menu section input { - flex: 1 1 100%; - background: rgba(0,0,0,0.6); - border-radius: var(--image-feed-radius); - overflow: hidden; - z-index: 100; - } .sizing-menu { position: relative; @@ -251,6 +232,27 @@ $el("style", { .pysssss-image-feed-handle:hover, .pysssss-image-feed-handle:active { background-color: var(--border-color); } + + .pysssss-image-feed-table td { + text-align: right; + } + .pysssss-image-feed-table td.right { + text-align: left; + } + .pysssss-image-feed-table select { + color: var(--fg-color); + border: 1px solid var(--border-color); + border-radius: 5px; + width: 100%; + background-color: var(--comfy-input-bg); + } + .pysssss-image-feed-table select:hover { + filter: brightness(1.2); + } + .pysssss-image-feed-table select:focus { + outline: none !important; + box-shadow: 0 0 3px var(--border-color); + } `, parent: document.body, }); @@ -280,6 +282,14 @@ app.registerExtension({ localStorage.setItem("pysssss.ImageFeed." + n, v); }; + const getValString = (n, d) => { + const v = localStorage.getItem("pysssss.ImageFeed." + n); + if (v) { + return v; + } + return d; + } + const imageFeedRoot = $el("div.pysssss-image-feed-root", { parent: document.body, }); @@ -292,46 +302,7 @@ app.registerExtension({ imageFeedRoot.append(imageFeed, resizeHandle); - const feedLocation = app.ui.settings.addSetting({ - id: "pysssss.ImageFeed.Location", - name: "🐍 Image Feed Location", - defaultValue: "bottom", - type: () => { - return $el("tr", [ - $el("td", [ - $el("label", { - textContent: "🐍 Image Feed Location:", - }), - ]), - $el("td", [ - $el( - "select", - { - style: { - fontSize: "14px", - }, - oninput: (e) => { - feedLocation.value = e.target.value; - onFeedLocationChange(feedLocation.value); - }, - }, - ["left", "top", "right", "bottom"].map((m) => - $el("option", { - value: m, - textContent: m, - selected: feedLocation.value === m, - }) - ) - ), - ]), - ]); - }, - onChange(value) { - onFeedLocationChange(value); - }, - }); - - function onFeedLocationChange(value) { + function updateFeedLocation(value) { imageFeed.className = `pysssss-image-feed pysssss-image-feed--${value}`; imageFeedRoot.className = `pysssss-image-feed-root pysssss-image-feed-root--${value}`; resizeHandle.className = `pysssss-image-feed-handle pysssss-image-feed-handle--${value}`; @@ -342,41 +313,7 @@ app.registerExtension({ } } - const feedDirection = app.ui.settings.addSetting({ - id: "pysssss.ImageFeed.Direction", - name: "🐍 Image Feed Direction", - defaultValue: "newest first", - type: () => { - return $el("tr", [ - $el("td", [ - $el("label", { - textContent: "🐍 Image Feed Direction:", - }), - ]), - $el("td", [ - $el( - "select", - { - style: { - fontSize: "14px", - }, - oninput: (e) => { - feedDirection.value = e.target.value; - imageList.replaceChildren(...[...imageList.childNodes].reverse()); - }, - }, - ["newest first", "oldest first"].map((m) => - $el("option", { - value: m, - textContent: m, - selected: feedDirection.value === m, - }) - ) - ), - ]), - ]); - }, - }); + updateFeedLocation(getValString("Location", "bottom")); const clearButton = $el("button.pysssss-image-feed-btn.clear-btn", { textContent: "Clear", @@ -405,12 +342,16 @@ app.registerExtension({ activeOnHover: true, }); settingsPopup.setContent( (content) => { - content.append( - $el("section.size-control.feed-size-control", {}, [ - $el("span", { - textContent: "Image Size", - }), - $el("input", { + const table = $el("table.pysssss-image-feed-table"); + + table.append( + $el("tr", [ + $el("td", [ + $el("label", { + textContent: "Image Size" + }), + ]), + $el("input.image-feed-image-size", { type: "range", min: 32, max: 512, @@ -429,7 +370,59 @@ app.registerExtension({ }, }), ]), - ) + $el("tr", [ + $el("td", [ + $el("label", { + textContent: "Location", + }), + ]), + $el("td.right", [ + $el( + "select", + { + oninput: (e) => { + saveVal("Location", e.target.value); + updateFeedLocation(getValString("Location", "bottom")); + }, + }, + ["left", "top", "right", "bottom"].map((m) => + $el("option", { + value: m, + textContent: m, + selected: getValString("Location", "bottom") === m, + }) + ) + ), + ]), + ]), + $el("tr", [ + $el("td", [ + $el("label", { + textContent: "Sort by", + }), + ]), + $el("td.right", [ + $el( + "select", + { + oninput: (e) => { + saveVal("Direction", e.target.value); + imageList.replaceChildren(...[...imageList.childNodes].reverse()); + }, + }, + ["newest", "oldest"].map((m) => + $el("option", { + value: m, + textContent: m, + selected: getValString("Direction", "newest") === m, + }) + ) + ), + ]), + ]), + ); + + content.append(table); }); imageFeed.append( @@ -458,7 +451,7 @@ app.registerExtension({ src.type }&subfolder=${encodeURIComponent(src.subfolder)}&t=${+new Date()}`; - const method = feedDirection.value === "newest first" ? "prepend" : "append"; + const method = getValString("Direction", "newest") === "newest" ? "prepend" : "append"; imageList[method]( $el("div", [ $el( @@ -492,7 +485,7 @@ app.registerExtension({ resizeHandle.addEventListener('mousedown', (e) => { isResizing = true; - isVertical = ["left", "right"].includes(feedLocation.value); + isVertical = ["left", "right"].includes(getValString("Location", "bottom")); if (isVertical) { initialSize = e.clientX; @@ -520,12 +513,13 @@ app.registerExtension({ } var newSize = 0; + const feedLocation = getValString("Location", "bottom"); if (isVertical) { const deltaX = e.clientX - initialSize; - newSize = feedLocation.value == "left" ? imageFeedRootSize + deltaX : imageFeedRootSize - deltaX; + newSize = feedLocation == "left" ? imageFeedRootSize + deltaX : imageFeedRootSize - deltaX; } else { const deltaY = e.clientY - initialSize; - newSize = feedLocation.value == "top" ? imageFeedRootSize + deltaY : imageFeedRootSize - deltaY; + newSize = feedLocation == "top" ? imageFeedRootSize + deltaY : imageFeedRootSize - deltaY; } /* Change image size */ @@ -536,7 +530,7 @@ app.registerExtension({ saveVal("ImageSize", newImageSize); imageFeedRoot.style.setProperty("--image-size", `${newImageSize}px`); /* Update settings slider */ - document.querySelector("section.size-control.feed-size-control input").value = newImageSize; + document.querySelector("input.image-feed-image-size").value = newImageSize; } /* Snap to closest image-size multiple for a cleaner list */ From c8ab4b8e18a97d13ceeb6a875dec1d759a8d727b Mon Sep 17 00:00:00 2001 From: birdddev <47731506+birdddev@users.noreply.github.com> Date: Thu, 14 Sep 2023 11:50:09 +0000 Subject: [PATCH 10/13] Update README.md Update image feed stuff --- README.md | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b3a7f83..56d216a 100644 --- a/README.md +++ b/README.md @@ -64,9 +64,19 @@ Adds a custom color picker to nodes & groups Adds a favicon and title to the window, favicon changes color while generating and the window title includes the number of prompts in the queue ## Image Feed -![image](https://github.com/pythongosssss/ComfyUI-Custom-Scripts/assets/125205205/caea0d48-85b9-4ca9-9771-5c795db35fbc) -Adds a panel showing images that have been generated in the current session, you can control the direction that images are added and the position of the panel via the ComfyUI settings screen and the size of the panel and the images via the sliders at the top of the panel. -![image](https://github.com/pythongosssss/ComfyUI-Custom-Scripts/assets/125205205/ca093d38-41a3-4647-9223-5bd0b9ee4f1e) +![image](https://github.com/birdddev/ComfyUI-Custom-Scripts/assets/47731506/b9f96e8b-c891-459d-b5c4-b43432caf880) +Adds a panel showing images that have been generated in the current session, you can sort the images that are added by newest or oldest and change the position of the panel via the panel's settings. + + +![image](https://github.com/birdddev/ComfyUI-Custom-Scripts/assets/47731506/1e4dbc2b-7484-43ee-9778-f4dcaddee913) + + +Holding Shift while adjusting the panel size also adjusts the size of the images. +While holding Ctrl, the panel will snap to the closest amount of "columns" the image size allows. + + +https://github.com/birdddev/ComfyUI-Custom-Scripts/assets/47731506/78707e01-2053-438c-905d-ce6a1578b53b + ## KSampler (Advanced) denoise helper Provides a simple method to set custom denoise on the advanced sampler From 7edf9176837d3ea8eb69b7071f134122c6b3122d Mon Sep 17 00:00:00 2001 From: bird_d Date: Thu, 14 Sep 2023 08:16:28 -0400 Subject: [PATCH 11/13] Image Feed: Fix list not taking up maximum space --- web/js/imageFeed.js | 1 + 1 file changed, 1 insertion(+) diff --git a/web/js/imageFeed.js b/web/js/imageFeed.js index 68fbaa0..9b3fa8b 100644 --- a/web/js/imageFeed.js +++ b/web/js/imageFeed.js @@ -159,6 +159,7 @@ $el("style", { min-height: 24px; } .pysssss-image-feed-list { + flex: 1; align-content: flex-start; overflow-y: auto; display: grid; From 1e8f6b5aec508e72f166368a871f7797256dbfba Mon Sep 17 00:00:00 2001 From: bird_d Date: Thu, 14 Sep 2023 16:53:07 -0400 Subject: [PATCH 12/13] Image Feed: Move css to file and cleanup --- web/js/imageFeed.css | 197 +++++++++++++++++++++++++++++++++ web/js/imageFeed.js | 258 +------------------------------------------ 2 files changed, 201 insertions(+), 254 deletions(-) create mode 100644 web/js/imageFeed.css diff --git a/web/js/imageFeed.css b/web/js/imageFeed.css new file mode 100644 index 0000000..964bc69 --- /dev/null +++ b/web/js/imageFeed.css @@ -0,0 +1,197 @@ +.pysssss-image-feed-root { + --image-size: 128px; + --image-gap-size: 4px; + + --image-feed-radius: 5px; + --image-feed-theme-handle: #333; + + + align-items: stretch; + display: flex; + font-size: 12px; + position: absolute; + text-align: center; + vertical-align: top; + z-index: 99; +} +.pysssss-image-feed-root--left, .pysssss-image-feed-root--right { + top: 0; + height: 100vh; +} +.pysssss-image-feed-root--left { + left: 0; +} +.pysssss-image-feed-root--right { + right: 0; + flex-direction: row-reverse; +} +.pysssss-image-feed-root--top, .pysssss-image-feed-root--bottom { + left: 0; + width: 100vw; +} +.pysssss-image-feed-root--top { + top: 0; + flex-direction: column; +} +.pysssss-image-feed-root--bottom { + bottom: 0; + flex-direction: column-reverse; +} +.pysssss-image-feed { + flex: 1; + background: var(--comfy-menu-bg); + color: var(--fg-color); + font-family: sans-serif; + font-size: 12px; + display: flex; + flex-direction: column; +} +.pysssss-image-feed--left, .pysssss-image-feed--right { + top: 0; + height: 100vh; +} + +.pysssss-image-feed--top, .pysssss-image-feed--bottom { + left: 0; + width: 100vw; +} + +.pysssss-image-feed-header { + position: relative; + display: flex; + gap: 5px; + padding: 5px; + justify-content: space-between; +} +.pysssss-image-feed-btn-group { + align-items: stretch; + display: flex; + gap: .5rem; + flex: 0 1 fit-content; + justify-content: flex-end; +} +.pysssss-image-feed-btn { + background-color:var(--comfy-input-bg); + border-radius: var(--image-feed-radius); + border:2px solid var(--border-color); + color: var(--fg-color); + cursor:pointer; + display:inline-block; + flex: 0 1 fit-content; + text-decoration:none; +} +.pysssss-image-feed-btn.sizing-btn:checked { + filter: invert(); +} +.pysssss-image-feed-btn.clear-btn { + padding: 5px 20px; +} +.pysssss-image-feed-btn.hide-btn { + padding: 5px; + aspect-ratio: 1 / 1; +} +.pysssss-image-feed-btn:hover { + filter: brightness(1.2); +} +.pysssss-image-feed-btn:active { + position:relative; + top:1px; +} + +.pysssss-image-feed-header > * { + min-height: 24px; +} +.pysssss-image-feed-list { + flex: 1; + align-content: flex-start; + overflow-y: auto; + display: grid; + gap: var(--image-gap-size); + justify-items: center; + transition: 100ms linear; + scrollbar-gutter: stable both-edges; + padding: 5px; + background: var(--comfy-input-bg); + border-radius: var(--image-feed-radius); + margin: 5px; + margin-top: 0px; +} +.pysssss-image-feed-list div, .pysssss-image-feed-list a { + display: flex; + flex-wrap: wrap; + width: var(--image-size); + height: var(--image-size); + align-content: center; + justify-content: center; +} +.pysssss-image-feed-list div { + background: rgba(0,0,0,0.15); + box-sizing: border-box; +} +.pysssss-image-feed-list div:hover{ + filter: brightness(1.2); +} +.pysssss-image-feed-list img { + max-width: 100%; + max-height: 100%; +} +.pysssss-image-feed-list::-webkit-scrollbar { + background: var(--comfy-input-bg); + border-radius: var(--image-feed-radius); +} +.pysssss-image-feed-list::-webkit-scrollbar-thumb { + background:var(--comfy-menu-bg); + border: 5px solid transparent; + border-radius: var(--image-feed-radius); + background-clip: content-box; +} +.pysssss-image-feed-list::-webkit-scrollbar-thumb:hover { + background: var(--border-color); + background-clip: content-box; +} +.pysssss-image-feed-handle { + flex: 0; + background-color: #191919; + display: flex; + align-items: center; + justify-content: center; + + /* Disables dragging so it doesn't mess up the grab. */ + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} +.pysssss-image-feed-handle--left, .pysssss-image-feed-handle--right { + cursor: col-resize; + min-width: 8px; + height: 100vh; +} +.pysssss-image-feed-handle--top, .pysssss-image-feed-handle--bottom { + cursor: row-resize; + min-height: 8px; + width: 100vw; +} +.pysssss-image-feed-handle:hover, .pysssss-image-feed-handle:active { + background-color: var(--border-color); +} + +.pysssss-image-feed-table td { + text-align: right; +} +.pysssss-image-feed-table td.right { + text-align: left; +} +.pysssss-image-feed-table select { + color: var(--fg-color); + border: 1px solid var(--border-color); + border-radius: 5px; + width: 100%; + background-color: var(--comfy-input-bg); +} +.pysssss-image-feed-table select:hover { + filter: brightness(1.2); +} +.pysssss-image-feed-table select:focus { + outline: none !important; + box-shadow: 0 0 3px var(--border-color); +} \ No newline at end of file diff --git a/web/js/imageFeed.js b/web/js/imageFeed.js index 9b3fa8b..47af1cd 100644 --- a/web/js/imageFeed.js +++ b/web/js/imageFeed.js @@ -3,260 +3,9 @@ import { app } from "../../../scripts/app.js"; import { $el } from "../../../scripts/ui.js"; import { lightbox } from "./common/lightbox.js"; import { PopUp } from "./common/popup.js"; +import { addStylesheet } from "./common/utils.js"; -$el("style", { - textContent: ` - .pysssss-image-feed-root { - --image-size: 128px; - --image-gap-size: 4px; - - --image-feed-radius: 5px; - --image-feed-theme-handle: #333; - - - align-items: stretch; - display: flex; - font-size: 12px; - position: absolute; - text-align: center; - vertical-align: top; - z-index: 99; - } - .pysssss-image-feed-root--left, .pysssss-image-feed-root--right { - top: 0; - height: 100vh; - } - .pysssss-image-feed-root--left { - left: 0; - } - .pysssss-image-feed-root--right { - right: 0; - flex-direction: row-reverse; - } - .pysssss-image-feed-root--top, .pysssss-image-feed-root--bottom { - left: 0; - width: 100vw; - } - .pysssss-image-feed-root--top { - top: 0; - flex-direction: column; - } - .pysssss-image-feed-root--bottom { - bottom: 0; - flex-direction: column-reverse; - } - .pysssss-image-feed { - flex: 1; - background: var(--comfy-menu-bg); - color: var(--fg-color); - font-family: sans-serif; - font-size: 12px; - display: flex; - flex-direction: column; - width: 100%; - height: 100%; - } - .pysssss-image-feed--left, .pysssss-image-feed--right { - top: 0; - height: 100vh; - width: 100%; - } - - .pysssss-image-feed--top, .pysssss-image-feed--bottom { - left: 0; - width: 100vw; - height: 100%; - } - - .pysssss-image-feed-menu { - position: relative; - flex: 0 1 min-content; - display: flex; - gap: 5px; - padding: 5px; - justify-content: space-between; - } - .pysssss-image-feed-btn-group { - align-items: stretch; - display: flex; - gap: .5rem; - flex: 0 1 fit-content; - justify-content: flex-end; - } - .pysssss-image-feed-btn { - background-color:var(--comfy-input-bg); - border-radius: var(--image-feed-radius); - border:2px solid var(--border-color); - color: var(--fg-color); - cursor:pointer; - display:inline-block; - flex: 0 1 fit-content; - text-decoration:none; - } - .pysssss-image-feed-btn.sizing-btn:checked { - filter: invert(); - } - .pysssss-image-feed-btn.clear-btn { - padding: 5px 20px; - } - .pysssss-image-feed-btn.hide-btn { - padding: 5px; - aspect-ratio: 1 / 1; - } - .pysssss-image-feed-btn:hover { - filter: brightness(1.2); - } - .pysssss-image-feed-btn:active { - position:relative; - top:1px; - } - - .sizing-menu { - position: relative; - } - - .size-controls-flyout { - position: absolute; - transform: scaleX(0%); - transition: 200ms ease-out; - transition-delay: 500ms; - z-index: 101; - width: 300px; - } - - .sizing-menu:hover .size-controls-flyout { - position: absolute; - transform: scale(1, 1); - transition: 200ms linear; - transition-delay: 0; - } - .pysssss-image-feed--bottom .size-controls-flyout { - transform: scale(1,0); - transform-origin: bottom; - bottom: 0; - left: 0; - } - .pysssss-image-feed--top .size-controls-flyout { - transform: scale(1,0); - transform-origin: top; - top: 0; - left: 0; - } - .pysssss-image-feed--left .size-controls-flyout { - transform: scale(0, 1); - transform-origin: left; - top: 0; - left: 0; - } - .pysssss-image-feed--right .size-controls-flyout { - transform: scale(0, 1); - transform-origin: right; - top: 0; - right: 0; - } - - .pysssss-image-feed-menu > * { - min-height: 24px; - } - .pysssss-image-feed-list { - flex: 1; - align-content: flex-start; - overflow-y: auto; - display: grid; - gap: var(--image-gap-size); - align-items: center; - justify-items: center; - transition: 100ms linear; - scrollbar-gutter: stable both-edges; - padding: 5px; - background: var(--comfy-input-bg); - border-radius: var(--image-feed-radius); - margin: 5px; - margin-top: 0px; - height: 100%; - } - .pysssss-image-feed-list div, .pysssss-image-feed-list a { - display: flex; - flex-wrap: wrap; - width: var(--image-size); - height: var(--image-size); - align-content: center; - justify-content: center; - } - .pysssss-image-feed-list div { - background: rgba(0,0,0,0.15); - box-sizing: border-box; - } - .pysssss-image-feed-list div:hover{ - filter: brightness(1.2); - } - .pysssss-image-feed-list img { - max-width: 100%; - max-height: 100%; - } - .pysssss-image-feed-list::-webkit-scrollbar { - background: var(--comfy-input-bg); - border-radius: var(--image-feed-radius); - } - .pysssss-image-feed-list::-webkit-scrollbar-thumb { - background:var(--comfy-menu-bg); - border: 5px solid transparent; - border-radius: var(--image-feed-radius); - background-clip: content-box; - } - .pysssss-image-feed-list::-webkit-scrollbar-thumb:hover { - background: var(--border-color); - background-clip: content-box; - } - .pysssss-image-feed-handle { - flex: 0; - background-color: #191919; - display: flex; - align-items: center; - justify-content: center; - - /* Disables dragging so it doesn't mess up the grab. */ - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - } - .pysssss-image-feed-handle--left, .pysssss-image-feed-handle--right { - cursor: col-resize; - min-width: 8px; - height: 100vh; - } - .pysssss-image-feed-handle--top, .pysssss-image-feed-handle--bottom { - cursor: row-resize; - min-height: 8px; - width: 100vw; - } - .pysssss-image-feed-handle:hover, .pysssss-image-feed-handle:active { - background-color: var(--border-color); - } - - .pysssss-image-feed-table td { - text-align: right; - } - .pysssss-image-feed-table td.right { - text-align: left; - } - .pysssss-image-feed-table select { - color: var(--fg-color); - border: 1px solid var(--border-color); - border-radius: 5px; - width: 100%; - background-color: var(--comfy-input-bg); - } - .pysssss-image-feed-table select:hover { - filter: brightness(1.2); - } - .pysssss-image-feed-table select:focus { - outline: none !important; - box-shadow: 0 0 3px var(--border-color); - } - `, - parent: document.body, -}); +addStylesheet(import.meta.url); app.registerExtension({ name: "pysssss.ImageFeed", @@ -427,7 +176,7 @@ app.registerExtension({ }); imageFeed.append( - $el("div.pysssss-image-feed-menu", [ + $el("div.pysssss-image-feed-header", [ settingsButton, $el("div.pysssss-image-feed-btn-group", {}, [clearButton, hideButton]), ]), @@ -543,6 +292,7 @@ app.registerExtension({ newSize = newSize + paddingSize + (steps * (gapSize + 2)); } + /* Make sure panel size is always within window */ if (newSize >= 0.0 && newSize <= isVertical ? window.innerWidth : window.innerHeight) { if (isVertical) { imageFeedRoot.style.minWidth = `${newSize}px`; From 7d442a92c64751939099d27528ce2ae3e03aba37 Mon Sep 17 00:00:00 2001 From: bird_d Date: Thu, 14 Sep 2023 17:05:55 -0400 Subject: [PATCH 13/13] Image Feed: Make bot/right image sizing consistent Every side now makes the image size bigger when pulling the panel out --- web/js/imageFeed.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/web/js/imageFeed.js b/web/js/imageFeed.js index 47af1cd..b2218ac 100644 --- a/web/js/imageFeed.js +++ b/web/js/imageFeed.js @@ -232,10 +232,14 @@ app.registerExtension({ let imageFeedRootSize = 0; let paddingSize = 0; let isVertical = false; + let isOpposite = false; resizeHandle.addEventListener('mousedown', (e) => { isResizing = true; - isVertical = ["left", "right"].includes(getValString("Location", "bottom")); + + const feedLocation = getValString("Location", "bottom"); + isVertical = ["left", "right"].includes(feedLocation); + isOpposite = ["right", "bottom"].includes(feedLocation); if (isVertical) { initialSize = e.clientX; @@ -274,7 +278,10 @@ app.registerExtension({ /* Change image size */ if (e.shiftKey && !e.ctrlKey) { - let newImageSize = parseInt(getVal("ImageSize")) + (isVertical ? e.movementX : e.movementY); + const axis = isVertical ? "movementX" : "movementY"; + + let newImageSize = parseInt(getVal("ImageSize")); + newImageSize += isOpposite ? -e[axis] : e[axis]; newImageSize = Math.min(Math.max(32, newImageSize), 512); saveVal("ImageSize", newImageSize);