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
This commit is contained in:
JaredTherriault
2024-01-16 20:10:11 -08:00
parent 2260b3a12c
commit cb7f5bc9ca
4 changed files with 174 additions and 82 deletions
+9 -20
View File
@@ -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 = `<lora:${loraNameText}:1:1>`;
if (trainedWords.length > 0) {
//Copy all
let copyAllText = copyLoraText + ", " + trainedWords;
buttonsRow.appendChild(
createButton(
$el("label", {
textContent: "📋",
}),
`Copy lora as a1111-style text + trained words (${"<lora:" + loraNameText + ":1.0:1.0>, " + trainedWords})`,
`Copy lora as a1111-style text + trained words (${copyAllText})`,
function(e) {
copyToClipboard("<lora:" + loraNameText + ":1.0:1.0>, " + trainedWords)
copyToClipboard(copyAllText)
e.preventDefault();
}
)
@@ -237,9 +226,9 @@ export async function createExtraNetworkCard(loraNameText, familiars) {
$el("label", {
textContent: "📜",
}),
`Copy lora as a1111-style text (${"<lora:" + loraNameText + ":1.0:1.0>"})`,
`Copy lora as a1111-style text (${copyLoraText})`,
function(e) {
copyToClipboard("<lora:" + loraNameText + ":1.0:1.0>")
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',
+142 -59
View File
@@ -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;
}
+6 -2
View File
@@ -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) {
+17 -1
View File
@@ -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;
}