add prompt history as suggested in #87
Signed-off-by: Victor Seiji Hariki <victorseijih@gmail.com>
This commit is contained in:
parent
34b995998b
commit
0a73687556
14 changed files with 526 additions and 81 deletions
231
css/index.css
231
css/index.css
|
@ -331,13 +331,48 @@ input#host {
|
||||||
|
|
||||||
/* Prompt Fields */
|
/* Prompt Fields */
|
||||||
|
|
||||||
|
.content.prompt {
|
||||||
|
display: flex;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content.prompt > .inputs {
|
||||||
|
width: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
div.prompt-wrapper {
|
div.prompt-wrapper {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
width: calc(100%);
|
||||||
|
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.prompt-wrapper > textarea {
|
div.prompt-wrapper > * {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.prompt-wrapper textarea {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
|
||||||
|
border-radius: 0;
|
||||||
|
|
||||||
|
border: none;
|
||||||
|
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.prompt-wrapper:not(:first-child) textarea {
|
||||||
|
border-top: 1px solid black;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.prompt-wrapper > textarea {
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: calc(100% - 20px);
|
||||||
|
|
||||||
|
padding: 2px;
|
||||||
|
|
||||||
|
transition-duration: 200ms;
|
||||||
|
|
||||||
resize: vertical;
|
resize: vertical;
|
||||||
}
|
}
|
||||||
|
@ -346,6 +381,198 @@ div.prompt-wrapper > textarea:focus {
|
||||||
width: 700px;
|
width: 700px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.prompt-wrapper > .prompt-indicator {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
cursor: help;
|
||||||
|
|
||||||
|
width: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.prompt-wrapper:first-child > .prompt-indicator {
|
||||||
|
border-top-left-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.prompt-wrapper:last-child > .prompt-indicator {
|
||||||
|
border-bottom-left-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.prompt-wrapper > .prompt-indicator.positive {
|
||||||
|
background-color: #484;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.prompt-wrapper > .prompt-indicator.negative {
|
||||||
|
background-color: #844;
|
||||||
|
}
|
||||||
|
div.prompt-wrapper > .prompt-indicator.styles {
|
||||||
|
background-color: #448;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.prompt-wrapper > .prompt-indicator::after {
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
margin: auto;
|
||||||
|
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
|
||||||
|
background-color: var(--c-text);
|
||||||
|
|
||||||
|
mask-size: contain;
|
||||||
|
-webkit-mask-size: contain;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.prompt-wrapper > .prompt-indicator.positive::after {
|
||||||
|
mask-image: url("/res/icons/plus-square.svg");
|
||||||
|
-webkit-mask-image: url("/res/icons/plus-square.svg");
|
||||||
|
}
|
||||||
|
|
||||||
|
div.prompt-wrapper > .prompt-indicator.negative::after {
|
||||||
|
mask-image: url("/res/icons/minus-square.svg");
|
||||||
|
-webkit-mask-image: url("/res/icons/minus-square.svg");
|
||||||
|
}
|
||||||
|
|
||||||
|
div.prompt-wrapper > .prompt-indicator.styles::after {
|
||||||
|
mask-image: url("/res/icons/library.svg");
|
||||||
|
-webkit-mask-image: url("/res/icons/library.svg");
|
||||||
|
}
|
||||||
|
|
||||||
|
.prompt-history-wrapper {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
width: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prompt-history-container {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#prompt-history {
|
||||||
|
width: 0px;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
transition-duration: 200ms;
|
||||||
|
|
||||||
|
background-color: #1e1e50;
|
||||||
|
}
|
||||||
|
|
||||||
|
#prompt-history.expanded {
|
||||||
|
width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#prompt-history .entry {
|
||||||
|
display: flex;
|
||||||
|
align-items: stretch;
|
||||||
|
justify-content: stretch;
|
||||||
|
|
||||||
|
border: 1px #fff3;
|
||||||
|
|
||||||
|
height: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#prompt-history.expanded .entry > button {
|
||||||
|
padding: 2px;
|
||||||
|
padding-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#prompt-history .entry > button {
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
margin: 0;
|
||||||
|
border: 0;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
color: var(--c-text);
|
||||||
|
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
|
transition-duration: 100ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
#prompt-history .entry:hover > button:not(:hover) {
|
||||||
|
flex-grow: 0;
|
||||||
|
flex-shrink: 1;
|
||||||
|
flex-basis: 20%;
|
||||||
|
width: 20%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#prompt-history .entry > button.prompt {
|
||||||
|
background-color: #484;
|
||||||
|
}
|
||||||
|
|
||||||
|
#prompt-history .entry > button.negative {
|
||||||
|
background-color: #844;
|
||||||
|
}
|
||||||
|
|
||||||
|
#prompt-history .entry > button.styles {
|
||||||
|
background-color: #448;
|
||||||
|
}
|
||||||
|
|
||||||
|
#prompt-history .entry > button:hover {
|
||||||
|
filter: brightness(115%);
|
||||||
|
backdrop-filter: brightness(115%);
|
||||||
|
}
|
||||||
|
|
||||||
|
#prompt-history .entry > button:active {
|
||||||
|
filter: brightness(150%);
|
||||||
|
backdrop-filter: brightness(150%);
|
||||||
|
}
|
||||||
|
|
||||||
|
button.prompt-history-btn {
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
border-radius: 0;
|
||||||
|
|
||||||
|
border-top-right-radius: 5px;
|
||||||
|
border-bottom-right-radius: 5px;
|
||||||
|
|
||||||
|
background-color: #1e1e50;
|
||||||
|
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
|
||||||
|
width: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.prompt-history-btn::after {
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
margin: auto;
|
||||||
|
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
|
||||||
|
background-color: var(--c-text);
|
||||||
|
|
||||||
|
mask-size: contain;
|
||||||
|
-webkit-mask-size: contain;
|
||||||
|
|
||||||
|
mask-image: url("/res/icons/history.svg");
|
||||||
|
-webkit-mask-image: url("/res/icons/history.svg");
|
||||||
|
}
|
||||||
|
|
||||||
|
button.prompt-history-btn:hover {
|
||||||
|
filter: brightness(115%);
|
||||||
|
}
|
||||||
|
|
||||||
|
button.prompt-history-btn:active {
|
||||||
|
filter: brightness(150%);
|
||||||
|
}
|
||||||
|
|
||||||
/* Style Field */
|
/* Style Field */
|
||||||
select > .style-select-option {
|
select > .style-select-option {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
37
index.html
37
index.html
|
@ -51,19 +51,31 @@
|
||||||
</label>
|
</label>
|
||||||
<!-- Prompts section -->
|
<!-- Prompts section -->
|
||||||
<button type="button" class="collapsible">Prompts</button>
|
<button type="button" class="collapsible">Prompts</button>
|
||||||
<div class="content">
|
<div class="content prompt">
|
||||||
<label for="prompt">Prompt:</label>
|
<div class="inputs">
|
||||||
<br />
|
<div class="prompt-wrapper">
|
||||||
<div class="prompt-wrapper">
|
<div class="prompt-indicator positive" title="Prompt"></div>
|
||||||
<textarea id="prompt"></textarea>
|
<textarea id="prompt" class="expandable"></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="prompt-wrapper">
|
||||||
|
<div
|
||||||
|
class="prompt-indicator negative"
|
||||||
|
title="Negative Prompt"></div>
|
||||||
|
<textarea id="negPrompt" class="expandable"></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="prompt-wrapper">
|
||||||
|
<div class="prompt-indicator styles" title="Styles"></div>
|
||||||
|
<div id="style-ac-mselect" style="flex-shrink: 1"></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<label for="negPrompt">Negative prompt:</label>
|
<div class="prompt-history-wrapper">
|
||||||
<div class="prompt-wrapper">
|
<div class="prompt-history-container">
|
||||||
<textarea id="negPrompt"></textarea>
|
<div id="prompt-history" class="expanded"></div>
|
||||||
|
<button
|
||||||
|
id="prompt-history-btn"
|
||||||
|
class="prompt-history-btn"></button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<label for="styleSelect">Styles:</label>
|
|
||||||
<div id="style-ac-mselect"></div>
|
|
||||||
<!-- <hr /> -->
|
|
||||||
</div>
|
</div>
|
||||||
<!-- SD section -->
|
<!-- SD section -->
|
||||||
<button type="button" class="collapsible">
|
<button type="button" class="collapsible">
|
||||||
|
@ -286,6 +298,7 @@
|
||||||
|
|
||||||
<!-- Base Libs -->
|
<!-- Base Libs -->
|
||||||
<script src="js/lib/util.js" type="text/javascript"></script>
|
<script src="js/lib/util.js" type="text/javascript"></script>
|
||||||
|
<script src="js/lib/events.js" type="text/javascript"></script>
|
||||||
<script src="js/lib/input.js" type="text/javascript"></script>
|
<script src="js/lib/input.js" type="text/javascript"></script>
|
||||||
<script src="js/lib/layers.js" type="text/javascript"></script>
|
<script src="js/lib/layers.js" type="text/javascript"></script>
|
||||||
<script src="js/lib/commands.js" type="text/javascript"></script>
|
<script src="js/lib/commands.js" type="text/javascript"></script>
|
||||||
|
@ -298,7 +311,9 @@
|
||||||
type="text/javascript"></script>
|
type="text/javascript"></script>
|
||||||
|
|
||||||
<!-- Content -->
|
<!-- Content -->
|
||||||
|
<script src="js/prompt.js" type="text/javascript"></script>
|
||||||
<script src="js/index.js" type="text/javascript"></script>
|
<script src="js/index.js" type="text/javascript"></script>
|
||||||
|
|
||||||
<script src="js/ui/floating/history.js" type="text/javascript"></script>
|
<script src="js/ui/floating/history.js" type="text/javascript"></script>
|
||||||
<script src="js/ui/floating/layers.js" type="text/javascript"></script>
|
<script src="js/ui/floating/layers.js" type="text/javascript"></script>
|
||||||
|
|
||||||
|
|
67
js/index.js
67
js/index.js
|
@ -96,20 +96,6 @@ function startup() {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const promptEl = document.getElementById("prompt");
|
|
||||||
promptEl.oninput = () => {
|
|
||||||
stableDiffusionData.prompt = promptEl.value;
|
|
||||||
promptEl.title = promptEl.value;
|
|
||||||
localStorage.setItem("prompt", stableDiffusionData.prompt);
|
|
||||||
};
|
|
||||||
|
|
||||||
const negPromptEl = document.getElementById("negPrompt");
|
|
||||||
negPromptEl.oninput = () => {
|
|
||||||
stableDiffusionData.negative_prompt = negPromptEl.value;
|
|
||||||
negPromptEl.title = negPromptEl.value;
|
|
||||||
localStorage.setItem("neg_prompt", stableDiffusionData.negative_prompt);
|
|
||||||
};
|
|
||||||
|
|
||||||
drawBackground();
|
drawBackground();
|
||||||
changeMaskBlur();
|
changeMaskBlur();
|
||||||
changeSmoothRendering();
|
changeSmoothRendering();
|
||||||
|
@ -447,12 +433,6 @@ const makeSlider = (
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const styleAutoComplete = createAutoComplete(
|
|
||||||
"Style",
|
|
||||||
document.getElementById("style-ac-mselect"),
|
|
||||||
{multiple: true}
|
|
||||||
);
|
|
||||||
|
|
||||||
const modelAutoComplete = createAutoComplete(
|
const modelAutoComplete = createAutoComplete(
|
||||||
"Model",
|
"Model",
|
||||||
document.getElementById("models-ac-select")
|
document.getElementById("models-ac-select")
|
||||||
|
@ -784,49 +764,6 @@ async function getConfig() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getStyles() {
|
|
||||||
/** @type {HTMLSelectElement} */
|
|
||||||
var styleSelect = document.getElementById("styleSelect");
|
|
||||||
var url = document.getElementById("host").value + "/sdapi/v1/prompt-styles";
|
|
||||||
try {
|
|
||||||
const response = await fetch(url);
|
|
||||||
/** @type {{name: string, prompt: string, negative_prompt: string}[]} */
|
|
||||||
const data = await response.json();
|
|
||||||
|
|
||||||
/** @type {string[]} */
|
|
||||||
let stored = null;
|
|
||||||
try {
|
|
||||||
stored = JSON.parse(localStorage.getItem("promptStyle"));
|
|
||||||
// doesn't seem to throw a syntaxerror if the localstorage item simply doesn't exist?
|
|
||||||
if (stored == null) stored = [];
|
|
||||||
} catch (e) {
|
|
||||||
stored = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
styleAutoComplete.options = data.map((style) => ({
|
|
||||||
name: style.name,
|
|
||||||
value: style.name,
|
|
||||||
title: `prompt: ${style.prompt}\nnegative: ${style.negative_prompt}`,
|
|
||||||
}));
|
|
||||||
styleAutoComplete.onchange.on(({value}) => {
|
|
||||||
let selected = [];
|
|
||||||
if (value.find((v) => v === "None")) {
|
|
||||||
styleAutoComplete.value = [];
|
|
||||||
} else {
|
|
||||||
selected = value;
|
|
||||||
}
|
|
||||||
stableDiffusionData.styles = selected;
|
|
||||||
localStorage.setItem("promptStyle", JSON.stringify(selected));
|
|
||||||
});
|
|
||||||
|
|
||||||
styleAutoComplete.value = stored;
|
|
||||||
localStorage.setItem("promptStyle", JSON.stringify(stored));
|
|
||||||
} catch (e) {
|
|
||||||
console.warn("[index] Failed to fetch prompt styles");
|
|
||||||
console.warn(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function changeStyles() {
|
function changeStyles() {
|
||||||
/** @type {HTMLSelectElement} */
|
/** @type {HTMLSelectElement} */
|
||||||
const styleSelectEl = document.getElementById("styleSelect");
|
const styleSelectEl = document.getElementById("styleSelect");
|
||||||
|
@ -958,10 +895,6 @@ function loadSettings() {
|
||||||
);
|
);
|
||||||
|
|
||||||
// set the values into the UI
|
// set the values into the UI
|
||||||
document.getElementById("prompt").value = String(_prompt);
|
|
||||||
document.getElementById("prompt").title = String(_prompt);
|
|
||||||
document.getElementById("negPrompt").value = String(_negprompt);
|
|
||||||
document.getElementById("negPrompt").title = String(_negprompt);
|
|
||||||
document.getElementById("maskBlur").value = Number(_mask_blur);
|
document.getElementById("maskBlur").value = Number(_mask_blur);
|
||||||
document.getElementById("seed").value = Number(_seed);
|
document.getElementById("seed").value = Number(_seed);
|
||||||
document.getElementById("cbxHRFix").checked = Boolean(_enable_hr);
|
document.getElementById("cbxHRFix").checked = Boolean(_enable_hr);
|
||||||
|
|
|
@ -48,7 +48,7 @@ for (var i = 0; i < coll.length; i++) {
|
||||||
if (active) content.style.maxHeight = content.scrollHeight + "px";
|
if (active) content.style.maxHeight = content.scrollHeight + "px";
|
||||||
});
|
});
|
||||||
|
|
||||||
Array.from(content.children).forEach((child) => {
|
Array.from(content.querySelectorAll("*")).forEach((child) => {
|
||||||
observer.observe(child);
|
observer.observe(child);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -62,6 +62,20 @@ for (var i = 0; i < coll.length; i++) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prompt history setup
|
||||||
|
*/
|
||||||
|
const _promptHistoryEl = document.getElementById("prompt-history");
|
||||||
|
const _promptHistoryBtn = document.getElementById("prompt-history-btn");
|
||||||
|
|
||||||
|
_promptHistoryEl.addEventListener("mouseleave", () => {
|
||||||
|
_promptHistoryEl.classList.remove("expanded");
|
||||||
|
});
|
||||||
|
|
||||||
|
_promptHistoryBtn.addEventListener("click", () =>
|
||||||
|
_promptHistoryEl.classList.toggle("expanded")
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Settings overlay setup
|
* Settings overlay setup
|
||||||
*/
|
*/
|
||||||
|
|
5
js/lib/events.js
Normal file
5
js/lib/events.js
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
const events = makeReadOnly({
|
||||||
|
tool: {
|
||||||
|
dream: new Observer(),
|
||||||
|
},
|
||||||
|
});
|
|
@ -80,6 +80,33 @@ const guid = (size = 3) => {
|
||||||
return id;
|
return id;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a hash code from a string
|
||||||
|
*
|
||||||
|
* From https://stackoverflow.com/questions/7616461/generate-a-hash-from-string-in-javascript
|
||||||
|
*
|
||||||
|
* @param {String} str The string to hash
|
||||||
|
* @return {Number} A 32bit integer
|
||||||
|
*/
|
||||||
|
const hashCode = (str, seed = 0) => {
|
||||||
|
let h1 = 0xdeadbeef ^ seed,
|
||||||
|
h2 = 0x41c6ce57 ^ seed;
|
||||||
|
for (let i = 0, ch; i < str.length; i++) {
|
||||||
|
ch = str.charCodeAt(i);
|
||||||
|
h1 = Math.imul(h1 ^ ch, 2654435761);
|
||||||
|
h2 = Math.imul(h2 ^ ch, 1597334677);
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 =
|
||||||
|
Math.imul(h1 ^ (h1 >>> 16), 2246822507) ^
|
||||||
|
Math.imul(h2 ^ (h2 >>> 13), 3266489909);
|
||||||
|
h2 =
|
||||||
|
Math.imul(h2 ^ (h2 >>> 16), 2246822507) ^
|
||||||
|
Math.imul(h1 ^ (h1 >>> 13), 3266489909);
|
||||||
|
|
||||||
|
return 4294967296 * (2097151 & h2) + (h1 >>> 0);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Assigns defaults to an option object passed to the function.
|
* Assigns defaults to an option object passed to the function.
|
||||||
*
|
*
|
||||||
|
|
189
js/prompt.js
Normal file
189
js/prompt.js
Normal file
|
@ -0,0 +1,189 @@
|
||||||
|
/**
|
||||||
|
* This file is for processing prompt/negative prompt and prompt style data
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Prompt Style Element
|
||||||
|
const styleSelectElement = createAutoComplete(
|
||||||
|
"Style",
|
||||||
|
document.getElementById("style-ac-mselect"),
|
||||||
|
{multiple: true}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Function to get styles from AUTOMATIC1111 webui
|
||||||
|
async function getStyles() {
|
||||||
|
var url = document.getElementById("host").value + "/sdapi/v1/prompt-styles";
|
||||||
|
try {
|
||||||
|
const response = await fetch(url);
|
||||||
|
/** @type {{name: string, prompt: string, negative_prompt: string}[]} */
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
/** @type {string[]} */
|
||||||
|
let stored = null;
|
||||||
|
try {
|
||||||
|
stored = JSON.parse(localStorage.getItem("promptStyle"));
|
||||||
|
// doesn't seem to throw a syntaxerror if the localstorage item simply doesn't exist?
|
||||||
|
if (stored == null) stored = [];
|
||||||
|
} catch (e) {
|
||||||
|
stored = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
styleSelectElement.options = data.map((style) => ({
|
||||||
|
name: style.name,
|
||||||
|
value: style.name,
|
||||||
|
title: `prompt: ${style.prompt}\nnegative: ${style.negative_prompt}`,
|
||||||
|
}));
|
||||||
|
styleSelectElement.onchange.on(({value}) => {
|
||||||
|
let selected = [];
|
||||||
|
if (value.find((v) => v === "None")) {
|
||||||
|
styleSelectElement.value = [];
|
||||||
|
} else {
|
||||||
|
selected = value;
|
||||||
|
}
|
||||||
|
stableDiffusionData.styles = selected;
|
||||||
|
localStorage.setItem("promptStyle", JSON.stringify(selected));
|
||||||
|
});
|
||||||
|
|
||||||
|
styleSelectElement.value = stored;
|
||||||
|
localStorage.setItem("promptStyle", JSON.stringify(stored));
|
||||||
|
} catch (e) {
|
||||||
|
console.warn("[index] Failed to fetch prompt styles");
|
||||||
|
console.warn(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
// Default configurations
|
||||||
|
const defaultPrompt =
|
||||||
|
"ocean floor scientific expedition, underwater wildlife";
|
||||||
|
const defaultNegativePrompt =
|
||||||
|
"people, person, humans, human, divers, diver, glitch, error, text, watermark, bad quality, blurry";
|
||||||
|
|
||||||
|
// Prompt Elements
|
||||||
|
const promptEl = document.getElementById("prompt");
|
||||||
|
const negativePromptEl = document.getElementById("negPrompt");
|
||||||
|
|
||||||
|
// Add prompt change handlers
|
||||||
|
promptEl.oninput = () => {
|
||||||
|
stableDiffusionData.prompt = promptEl.value;
|
||||||
|
promptEl.title = promptEl.value;
|
||||||
|
localStorage.setItem("prompt", stableDiffusionData.prompt);
|
||||||
|
};
|
||||||
|
|
||||||
|
negativePromptEl.oninput = () => {
|
||||||
|
stableDiffusionData.negative_prompt = negativePromptEl.value;
|
||||||
|
negativePromptEl.title = negativePromptEl.value;
|
||||||
|
localStorage.setItem("neg_prompt", stableDiffusionData.negative_prompt);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Load from local storage if set
|
||||||
|
const promptDefaultValue = localStorage.getItem("prompt") || defaultPrompt;
|
||||||
|
const negativePromptDefaultValue =
|
||||||
|
localStorage.getItem("neg_prompt") || defaultNegativePrompt;
|
||||||
|
|
||||||
|
promptEl.value = promptEl.title = promptDefaultValue;
|
||||||
|
negativePromptEl.value = negativePromptEl.title = negativePromptDefaultValue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prompt History
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Get history-related elements
|
||||||
|
const promptHistoryEl = document.getElementById("prompt-history");
|
||||||
|
|
||||||
|
// History
|
||||||
|
const history = [];
|
||||||
|
|
||||||
|
function syncPromptHistory() {
|
||||||
|
const historyCopy = Array.from(history);
|
||||||
|
historyCopy.reverse();
|
||||||
|
|
||||||
|
for (let i = 0; i < historyCopy.length; i++) {
|
||||||
|
const historyItem = historyCopy[i];
|
||||||
|
|
||||||
|
const id = `prompt-history-${historyItem.id}`;
|
||||||
|
if (promptHistoryEl.querySelector(`#${id}`)) break;
|
||||||
|
|
||||||
|
const historyEntry = document.createElement("div");
|
||||||
|
historyEntry.classList.add("entry");
|
||||||
|
historyEntry.id = id;
|
||||||
|
historyEntry.title = `prompt: ${historyItem.prompt}\nnegative: ${
|
||||||
|
historyItem.negative
|
||||||
|
}\nstyles: ${historyItem.styles.join(", ")}`;
|
||||||
|
|
||||||
|
// Compare with previous
|
||||||
|
const samePrompt =
|
||||||
|
i !== historyCopy.length - 1 &&
|
||||||
|
historyItem.prompt === historyCopy[i + 1].prompt;
|
||||||
|
const sameNegativePrompt =
|
||||||
|
i !== historyCopy.length - 1 &&
|
||||||
|
historyItem.negative === historyCopy[i + 1].negative;
|
||||||
|
const sameStyles =
|
||||||
|
i !== historyCopy.length - 1 &&
|
||||||
|
historyItem.styles.length === historyCopy[i + 1].styles.length &&
|
||||||
|
!historyItem.styles.some(
|
||||||
|
(v, index) => v !== historyCopy[i + 1].styles[index]
|
||||||
|
);
|
||||||
|
|
||||||
|
const prompt = historyItem.prompt;
|
||||||
|
const negative = historyItem.negative;
|
||||||
|
const styles = historyItem.styles;
|
||||||
|
|
||||||
|
const promptBtn = document.createElement("button");
|
||||||
|
promptBtn.classList.add("prompt");
|
||||||
|
promptBtn.addEventListener("click", () => {
|
||||||
|
stableDiffusionData.prompt = prompt;
|
||||||
|
promptEl.title = prompt;
|
||||||
|
promptEl.value = prompt;
|
||||||
|
localStorage.setItem("prompt", prompt);
|
||||||
|
});
|
||||||
|
promptBtn.textContent = (samePrompt ? "= " : "") + prompt;
|
||||||
|
|
||||||
|
const negativeBtn = document.createElement("button");
|
||||||
|
negativeBtn.classList.add("negative");
|
||||||
|
negativeBtn.addEventListener("click", () => {
|
||||||
|
stableDiffusionData.negative_prompt = negative;
|
||||||
|
negativePromptEl.title = negative;
|
||||||
|
negativePromptEl.value = negative;
|
||||||
|
localStorage.setItem("neg_prompt", negative);
|
||||||
|
});
|
||||||
|
negativeBtn.textContent = (sameNegativePrompt ? "= " : "") + negative;
|
||||||
|
|
||||||
|
const stylesBtn = document.createElement("button");
|
||||||
|
stylesBtn.classList.add("styles");
|
||||||
|
stylesBtn.textContent = (sameStyles ? "= " : "") + styles.join(", ");
|
||||||
|
stylesBtn.addEventListener("click", () => {
|
||||||
|
styleSelectElement.value = styles;
|
||||||
|
});
|
||||||
|
|
||||||
|
historyEntry.appendChild(promptBtn);
|
||||||
|
historyEntry.appendChild(negativeBtn);
|
||||||
|
historyEntry.appendChild(stylesBtn);
|
||||||
|
|
||||||
|
promptHistoryEl.insertBefore(historyEntry, promptHistoryEl.firstChild);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listen for dreaming to add to history
|
||||||
|
events.tool.dream.on((message) => {
|
||||||
|
const {event} = message;
|
||||||
|
if (event === "generate") {
|
||||||
|
const {prompt, negative_prompt, styles} = message.request;
|
||||||
|
const hash = hashCode(
|
||||||
|
`p: ${prompt}, n: ${negative_prompt}, s: ${JSON.stringify(styles)}`
|
||||||
|
);
|
||||||
|
if (
|
||||||
|
!history[history.length - 1] ||
|
||||||
|
history[history.length - 1].hash !== hash
|
||||||
|
)
|
||||||
|
history.push({
|
||||||
|
id: guid(),
|
||||||
|
hash,
|
||||||
|
prompt,
|
||||||
|
negative: negative_prompt,
|
||||||
|
styles,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
syncPromptHistory();
|
||||||
|
});
|
||||||
|
})();
|
|
@ -120,6 +120,8 @@ const _generate = async (
|
||||||
bb,
|
bb,
|
||||||
drawEvery = 0.2 / request.n_iter
|
drawEvery = 0.2 / request.n_iter
|
||||||
) => {
|
) => {
|
||||||
|
events.tool.dream.emit({event: "generate", request});
|
||||||
|
|
||||||
const requestCopy = JSON.parse(JSON.stringify(request));
|
const requestCopy = JSON.parse(JSON.stringify(request));
|
||||||
|
|
||||||
// Block requests to identical areas
|
// Block requests to identical areas
|
||||||
|
|
6
res/icons/history.svg
Normal file
6
res/icons/history.svg
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<path d="M3 3v5h5"></path>
|
||||||
|
<path d="M3.05 13A9 9 0 1 0 6 5.3L3 8"></path>
|
||||||
|
<path d="M12 7v5l4 2"></path>
|
||||||
|
|
||||||
|
</svg>
|
After Width: | Height: | Size: 299 B |
7
res/icons/library.svg
Normal file
7
res/icons/library.svg
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<path d="m16 6 4 14"></path>
|
||||||
|
<path d="M12 6v14"></path>
|
||||||
|
<path d="M8 8v12"></path>
|
||||||
|
<path d="M4 4v16"></path>
|
||||||
|
|
||||||
|
</svg>
|
After Width: | Height: | Size: 305 B |
5
res/icons/minus-square.svg
Normal file
5
res/icons/minus-square.svg
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
|
||||||
|
<line x1="8" y1="12" x2="16" y2="12"></line>
|
||||||
|
|
||||||
|
</svg>
|
After Width: | Height: | Size: 301 B |
4
res/icons/minus.svg
Normal file
4
res/icons/minus.svg
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<line x1="5" y1="12" x2="19" y2="12"></line>
|
||||||
|
|
||||||
|
</svg>
|
After Width: | Height: | Size: 236 B |
6
res/icons/plus-square.svg
Normal file
6
res/icons/plus-square.svg
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
|
||||||
|
<line x1="12" y1="8" x2="12" y2="16"></line>
|
||||||
|
<line x1="8" y1="12" x2="16" y2="12"></line>
|
||||||
|
|
||||||
|
</svg>
|
After Width: | Height: | Size: 348 B |
5
res/icons/plus.svg
Normal file
5
res/icons/plus.svg
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<line x1="12" y1="5" x2="12" y2="19"></line>
|
||||||
|
<line x1="5" y1="12" x2="19" y2="12"></line>
|
||||||
|
|
||||||
|
</svg>
|
After Width: | Height: | Size: 283 B |
Loading…
Reference in a new issue