From cb7f5bc9cab99bc04c6f7778d1fa0eaafead465f Mon Sep 17 00:00:00 2001 From: JaredTherriault Date: Tue, 16 Jan 2024 20:10:11 -0800 Subject: [PATCH] Copy positive_prompt from imageElement, select search text on context change, backend improvements -Move around some functions to be shared -Consolidate some text handling -Select search text when switching contexts -Add hover menu to imageElements (buttonToolbar) -Add button to copy 'positive_prompt' value from metadata (use the positive prompt node for compatibility) -Save metadata to imageElement -Move some element definitions around in imageElements so functions that rely on certain elements work correctly --- web/js/ImageDrawer/ExtraNetworks.js | 29 ++-- web/js/ImageDrawer/ImageElements.js | 201 ++++++++++++++++++++-------- web/js/ImageDrawer/imageDrawer.js | 8 +- web/js/common/utils.js | 18 ++- 4 files changed, 174 insertions(+), 82 deletions(-) diff --git a/web/js/ImageDrawer/ExtraNetworks.js b/web/js/ImageDrawer/ExtraNetworks.js index 27e8577..054f6a2 100644 --- a/web/js/ImageDrawer/ExtraNetworks.js +++ b/web/js/ImageDrawer/ExtraNetworks.js @@ -1,6 +1,6 @@ import { api } from "/scripts/api.js"; import { $el } from "/scripts/ui.js"; -import { copyToClipboard, setSearchTermsOnElement } from "../common/utils.js" +import { copyToClipboard, setSearchTermsOnElement, createDarkContainer, getDarkColor } from "../common/utils.js" import { setSearchTextAndExecute } from "./imageDrawer.js"; @@ -34,8 +34,6 @@ export async function createExtraNetworkCard(loraNameText, familiars) { let trainedWords = []; let tags = []; - const darkColor = 'rgba(0,0,0,0.5)'; - // Load the first image as the object cover image. // If one does not exist, fall back to placeholder image. function getHrefForFamiliarImage(index) { @@ -136,18 +134,6 @@ export async function createExtraNetworkCard(loraNameText, familiars) { }); backgroundImage.src = getHrefForFamiliarImage(backgroundImage.lastViewedImageIndex); - function createDarkContainer(identifier, paddingOverride) { - return $el("div", { - id: identifier, - style: { - position: 'absolute', - backgroundColor: darkColor, - display: 'inline-block', // Size to content, '-block' to allow vertical margin and padding - padding: paddingOverride ? paddingOverride : '1%', - }, - }); - } - let imageCounterLabel; function updateImageCounterText() { @@ -200,17 +186,20 @@ export async function createExtraNetworkCard(loraNameText, familiars) { return buttonElement; } + + let copyLoraText = ``; if (trainedWords.length > 0) { //Copy all + let copyAllText = copyLoraText + ", " + trainedWords; buttonsRow.appendChild( createButton( $el("label", { textContent: "📋", }), - `Copy lora as a1111-style text + trained words (${", " + trainedWords})`, + `Copy lora as a1111-style text + trained words (${copyAllText})`, function(e) { - copyToClipboard(", " + trainedWords) + copyToClipboard(copyAllText) e.preventDefault(); } ) @@ -237,9 +226,9 @@ export async function createExtraNetworkCard(loraNameText, familiars) { $el("label", { textContent: "📜", }), - `Copy lora as a1111-style text (${""})`, + `Copy lora as a1111-style text (${copyLoraText})`, function(e) { - copyToClipboard("") + copyToClipboard(copyLoraText) e.preventDefault(); } ) @@ -311,7 +300,7 @@ export async function createExtraNetworkCard(loraNameText, familiars) { top: '85%', left: 0, width: "100%", - backgroundColor: darkColor, + backgroundColor: getDarkColor(), height: '15%', display: 'flex', flexDirection: 'column', diff --git a/web/js/ImageDrawer/ImageElements.js b/web/js/ImageDrawer/ImageElements.js index ea73d35..d52f215 100644 --- a/web/js/ImageDrawer/ImageElements.js +++ b/web/js/ImageDrawer/ImageElements.js @@ -3,7 +3,7 @@ import { getPngMetadata } from "/scripts/pnginfo.js"; import { defaultKeyList, getVal } from "./imageDrawer.js"; // getValue only prepends 'JNodes.', getVal also prepends 'ImageDrawer.'. -import { getValue, setSearchTermsOnElement, getMaxZIndex, getLastMousePosition } from "../common/utils.js" +import { getValue, setSearchTermsOnElement, getMaxZIndex, getLastMousePosition, createDarkContainer, copyToClipboard } from "../common/utils.js" import ExifReader from '../common/ExifReader-main/src/exif-reader.js'; import { createModal } from "../common/modal.js"; @@ -74,6 +74,121 @@ export async function createImageElementFromImgSrc(src) { const href = `/view?filename=${encodeURIComponent(src.filename)}&type=${src.type}&subfolder=${encodeURIComponent(src.subfolder)}&t=${+new Date()}`; const bIsVideoFormat = src.format?.startsWith("video"); + const imageElement = + $el("div.imageElement", { + style: { +// textAlign: 'center', +// objectFit: 'var(--div-fit, contain)', +// height: 'calc(var(--max-size) * 1vh)', +// borderRadius: '4px', +// position: "relative", + maxWidth: 'fit-content', + } + }); + + function createButtonToolbar() { + + function createButtons() { + if (!buttonsRow) { return; } + + function createButton(foregroundElement, tooltipText, onClickFunction) { + const buttonElement = $el("button", { + title: tooltipText, + style: { + background: 'none', + border: 'none', + padding: 0, + } + }, [ + foregroundElement + ]); + + buttonElement.addEventListener('click', onClickFunction); + + return buttonElement; + } + + // Options button + buttonsRow.appendChild( + createButton( + $el("label", { + textContent: "📋", + }), + `Copy positive prompt`, + function(e) { + let positive_prompt = imageElement?.metadata?.positive_prompt; + if (positive_prompt.startsWith('"')) { positive_prompt = positive_prompt.slice(1); } + if (positive_prompt.endsWith('"')) { positive_prompt = positive_prompt.slice(0, positive_prompt.length - 1); } + copyToClipboard(positive_prompt); + e.preventDefault(); + } + ) + ); + } + + const buttonsRow = $el("div", { + style: { + width: '100%', + display: 'flex', + flexDirection: 'row', + } + }); + + const buttonToolbarContainerElement = createDarkContainer(); + + buttonToolbarContainerElement.style.top = '2%'; + buttonToolbarContainerElement.style.left = '2%'; + buttonToolbarContainerElement.style.visibility = "hidden"; + buttonToolbarContainerElement.appendChild(buttonsRow); + + createButtons(); + + return buttonToolbarContainerElement; + } + + const aElement = $el("a", { + target: "_blank", + href, + onclick: async (e) => { + + function createModalContent() { + const modalImg = $el(bIsVideoFormat ? "video" : "img", { + src: href, + // Store the image source as a data attribute for easy access + 'data-src': href, + style: { + position: 'relative', + width: '99vw', + height: '99vh', + maxWidth: 'fit-content', + maxHeight: 'fit-content', + display: "block", + margin: "auto", + }, + }); + + // Create modal content + const modalContent = document.createElement("div"); + modalContent.style.position = 'absolute'; + modalContent.style.display = "inline-block"; + modalContent.style.left = "50%"; + modalContent.style.top = "50%"; + modalContent.style.transform = "translate(-50%, -50%)"; + modalContent.style.maxWidth = "99%"; + modalContent.style.maxHeight = "99%"; + modalContent.style.overflow = "hidden"; + + modalContent.appendChild(modalImg); + + return modalContent; + } + e.preventDefault(); + createModal(createModalContent()); + } + }); + + imageElement.appendChild(aElement); + const img = $el(bIsVideoFormat ? "video" : "img", { src: href, // Store the image source as a data attribute for easy access @@ -229,23 +344,32 @@ export async function createImageElementFromImgSrc(src) { if (widget) { imageElement.tooltipWidget = widget; + + function mouseOverEvent() { + + updateAndShowTooltip(imageElement.tooltipWidget, imageElement); + buttonToolbar.style.visibility = "visible"; + } + + function mouseOutEvent() { + + if (!toolTip) { return; } + toolTip.style.visibility = "hidden"; + toolTip.style.opacity = "0"; + + buttonToolbar.style.visibility = "hidden"; + } const lastMousePosition = getLastMousePosition(); const elementUnderMouse = document.elementFromPoint(lastMousePosition[0], lastMousePosition[1]); if (elementUnderMouse && elementUnderMouse == img) { - updateAndShowTooltip(imageElement.tooltipWidget, imageElement); + mouseOverEvent(); } // Show/Hide tooltip - imageElement.addEventListener("mouseover", () => { - updateAndShowTooltip(imageElement.tooltipWidget, imageElement); - }); + imageElement.addEventListener("mouseover", mouseOverEvent); - imageElement.addEventListener("mouseout", () => { - if (!toolTip) { return; } - toolTip.style.visibility = "hidden"; - toolTip.style.opacity = "0"; - }); + imageElement.addEventListener("mouseout", mouseOutEvent); imageElement.onpointermove = e => { if (toolTip?.style?.visibility === "visible") { @@ -272,6 +396,8 @@ export async function createImageElementFromImgSrc(src) { const blob = await response.blob(); const pnginfo = await getPngMetadata(blob); + imageElement.metadata = pnginfo; + const toolTipWidget = makeTooltipWidgetFromMetadata(pnginfo); setTooltipFromWidget(toolTipWidget); @@ -304,6 +430,9 @@ export async function createImageElementFromImgSrc(src) { const jsonReadyString = cleanedString.replace("UNICODE", "") const asJson = JSON.parse(jsonReadyString); + + imageElement.metadata = asJson; + const toolTipWidget = makeTooltipWidgetFromMetadata(asJson); setTooltipFromWidget(toolTipWidget); @@ -325,55 +454,9 @@ export async function createImageElementFromImgSrc(src) { img.loop = true; } - const imageElement = - $el("div.imageElement", { - style: { - display: 'inline-block', - } - }, [ - $el("a", { - target: "_blank", - href, - onclick: async (e) => { - - function createModalContent() { - const modalImg = $el(bIsVideoFormat ? "video" : "img", { - src: href, - // Store the image source as a data attribute for easy access - 'data-src': href, - style: { - position: 'relative', - width: '99vw', - height: '99vh', - maxWidth: 'fit-content', - maxHeight: 'fit-content', - display: "block", - margin: "auto", - }, - }); - - // Create modal content - const modalContent = document.createElement("div"); - modalContent.style.position = 'absolute'; - modalContent.style.display = "inline-block"; - modalContent.style.left = "50%"; - modalContent.style.top = "50%"; - modalContent.style.transform = "translate(-50%, -50%)"; - modalContent.style.maxWidth = "99%"; - modalContent.style.maxHeight = "99%"; - modalContent.style.overflow = "hidden"; - - modalContent.appendChild(modalImg); - - return modalContent; - } - e.preventDefault(); - createModal(createModalContent()); - } - }, [ - img, - ]), - ]); + aElement.appendChild(img); + const buttonToolbar = createButtonToolbar(imageElement) + imageElement.appendChild(buttonToolbar); return imageElement; } \ No newline at end of file diff --git a/web/js/ImageDrawer/imageDrawer.js b/web/js/ImageDrawer/imageDrawer.js index c493613..275a637 100644 --- a/web/js/ImageDrawer/imageDrawer.js +++ b/web/js/ImageDrawer/imageDrawer.js @@ -359,8 +359,8 @@ const createContextSelector = () => { // Set up lastSelectedContextOption to accommodate future context switching lastSelectedContextOption = selectedValue; - // Automatically focus search bar to save user a click - focusSearch(); + // Automatically focus search bar and select text to save user a click + focusAndSelectSearchText(); }); }; @@ -408,6 +408,10 @@ export function focusSearch() { SearchBar.focus(); } +export function focusAndSelectSearchText() { + SearchBar.select(); // Select focuses already +} + // Function to execute seach with an explicit searchTerm export function executeSearch(searchTerm) { diff --git a/web/js/common/utils.js b/web/js/common/utils.js index d0e70a0..47a67b1 100644 --- a/web/js/common/utils.js +++ b/web/js/common/utils.js @@ -70,6 +70,22 @@ export function setValue(name, val) { localStorage.setItem("JNodes.Settings." + name, val); }; +export function getDarkColor() { + return 'rgba(0,0,0,0.5)'; +} + +export function createDarkContainer(identifier, paddingOverride) { + return $el("div", { + id: identifier, + style: { + position: 'absolute', + backgroundColor: getDarkColor(), + display: 'inline-block', // Size to content, '-block' to allow vertical margin and padding + padding: paddingOverride ? paddingOverride : '1%', + }, + }); +} + export function setSearchTermsOnElement(element, searchTerms) { element.setAttribute('searchTerms', searchTerms); } @@ -81,7 +97,7 @@ export function getMaxZIndex(element) { maxZIndex = Math.max(maxZIndex, parent.style.zIndex); parent = parent.parentElement; } - + return maxZIndex; }