Files
pythongosssss-ComfyUI-Custo…/web/js/imageFeed.js
T
bird_d 9aa9c3eadd Image Feed: Fix padding for bottom location
When feed is on the bottom, the top of the image feed gets padding.
In all other locations this would be extra padding, so only apply here.
2023-08-31 17:01:26 -04:00

418 lines
9.8 KiB
JavaScript

import { api } from "../../../scripts/api.js";
import { app } from "../../../scripts/app.js";
import { $el } from "../../../scripts/ui.js";
import { lightbox } from "./common/lightbox.js";
$el("style", {
textContent: `
.pysssss-image-feed {
position: absolute;
background: var(--comfy-menu-bg);
color: var(--fg-color);
z-index: 99;
font-family: sans-serif;
font-size: 12px;
display: flex;
flex-direction: column;
}
.pysssss-image-feed--top, .pysssss-image-feed--bottom {
width: 100vw;
min-height: 30px;
max-height: calc(var(--max-size, 20) * 1vh);
}
.pysssss-image-feed--top {
top: 0;
}
.pysssss-image-feed--bottom {
bottom: 0;
flex-direction: column-reverse;
padding-top: 5px;
}
.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;
}
.pysssss-image-feed--left .pysssss-image-feed-menu, .pysssss-image-feed--right .pysssss-image-feed-menu {
flex-direction: column;
}
.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:5px;
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-menu section {
border-radius: 5px;
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: 5px;
overflow: hidden;
z-index: 100;
}
.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 {
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 1 auto;
overflow-y: auto;
display: grid;
align-items: center;
justify-content: center;
gap: 4px;
grid-auto-rows: min-content;
grid-template-columns: repeat(var(--img-sz, 3), 1fr);
transition: 100ms linear;
scrollbar-gutter: stable both-edges;
padding: 5px;
background: var(--comfy-input-bg);
border-radius: 5px;
margin: 5px;
margin-top: 0px;
}
.pysssss-image-feed-list div {
height: 100%;
text-align: center;
}
.pysssss-image-feed-list::-webkit-scrollbar {
background: var(--comfy-input-bg);
border-radius: 5px;
}
.pysssss-image-feed-list::-webkit-scrollbar-thumb {
background:var(--comfy-menu-bg);
border: 5px solid transparent;
border-radius: 8px;
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-list img:hover {
filter: brightness(1.2);
}`,
parent: document.body,
});
app.registerExtension({
name: "pysssss.ImageFeed",
setup() {
let visible = true;
const showButton = $el("button.comfy-settings-btn", {
textContent: "🖼️",
style: {
right: "16px",
cursor: "pointer",
display: "none",
},
});
const getVal = (n, d) => {
const v = localStorage.getItem("pysssss.ImageFeed." + n);
if (v && !isNaN(+v)) {
return v;
}
return d;
};
const saveVal = (n, v) => {
localStorage.setItem("pysssss.ImageFeed." + n, v);
};
const imageFeed = $el("div.pysssss-image-feed", {
parent: document.body,
});
const imageList = $el("div.pysssss-image-feed-list");
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;
imageFeed.className = `pysssss-image-feed pysssss-image-feed--${feedLocation.value}`;
},
},
["left", "top", "right", "bottom"].map((m) =>
$el("option", {
value: m,
textContent: m,
selected: feedLocation.value === m,
})
)
),
]),
]);
},
onChange(value) {
imageFeed.className = `pysssss-image-feed pysssss-image-feed--${value}`;
},
});
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,
})
)
),
]),
]);
},
});
const clearButton = $el("button.pysssss-image-feed-btn.clear-btn", {
textContent: "Clear",
onclick: () => imageList.replaceChildren(),
});
const hideButton = $el("button.pysssss-image-feed-btn.hide-btn", {
textContent: "❌",
onclick: () => {
imageFeed.style.display = "none";
showButton.style.display = "unset";
saveVal("Visible", 0);
visible = false;
},
});
imageFeed.append(
$el("div.pysssss-image-feed-menu", [
$el("section.sizing-menu", {}, [
$el("label.size-control-handle", { textContent: "↹ Resize Feed" }),
$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 });
});
},
}),
]),
$el("section.size-control.image-size-control", {}, [
$el("span", { textContent: "Column count..." }),
$el("input", {
type: "range",
min: 1,
max: 10,
step: 1,
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);
saveVal("ImageSize", e.target.value);
},
$: (el) => {
requestAnimationFrame(() => {
el.value = getVal("ImageSize", 4);
el.oninput({ target: el });
});
},
}),
]),
]),
]),
$el("div.pysssss-image-feed-btn-group", {}, [clearButton, hideButton]),
]),
imageList
);
showButton.onclick = () => {
imageFeed.style.display = "block";
showButton.style.display = "none";
saveVal("Visible", 1);
visible = true;
};
document.querySelector(".comfy-settings-btn").after(showButton);
if (!+getVal("Visible", 1)) {
hideButton.onclick();
}
api.addEventListener("executed", ({ detail }) => {
if (visible && detail?.output?.images) {
for (const src of detail.output.images) {
const href = `/view?filename=${encodeURIComponent(src.filename)}&type=${
src.type
}&subfolder=${encodeURIComponent(src.subfolder)}&t=${+new Date()}`;
const method = feedDirection.value === "newest first" ? "prepend" : "append";
imageList[method](
$el("div", [
$el(
"a",
{
target: "_blank",
href,
onclick: (e) => {
const imgs = [...imageList.querySelectorAll("img")].map((img) => img.getAttribute("src"));
lightbox.show(imgs, imgs.indexOf(href));
e.preventDefault();
},
},
[$el("img", { src: href })]
),
])
);
}
}
});
},
});