toc / ex /dynamic-prompts /javascript /dynamic_prompting.js
Toc's picture
Upload 1820 files
8da9c80
raw
history blame
9.34 kB
/* global gradioApp, get_uiCurrentTabContent, onUiUpdate, onUiLoaded */
// prettier-ignore
const SDDP_HELP_TEXTS = {
"sddp-disable-negative-prompt": "Don't use prompt magic on negative prompts.",
"sddp-dynamic-prompts-enabled": "Complete documentation is available at https://github.com/adieyal/sd-dynamic-prompts. Please report any issues on GitHub.",
"sddp-is-attention-grabber": "Add emphasis to a randomly selected keyword in the prompt.",
"sddp-is-combinatorial": "Generate all possible prompt combinations.",
"sddp-is-feelinglucky": "Generate random prompts from lexica.art (your prompt is used as a search query).",
"sddp-is-fixed-seed": "Use the same seed for all prompts in this batch",
"sddp-is-magicprompt": "Automatically update your prompt with interesting modifiers. (Runs slowly the first time)",
"sddp-magic-prompt-model": "Note: Each model will download between 300mb and 1.4gb of data on first use.",
"sddp-no-image-generation": "Disable image generation. Useful if you only want to generate text prompts. (1 image will still be generated to keep Auto1111 happy.).",
"sddp-unlink-seed-from-prompt": "If this is set, then random prompts are generated, even if the seed is the same.",
"sddp-write-prompts": "Write all generated prompts to a file",
"sddp-write-raw-template": "Write template into image metadata.",
};
class SDDPTreeView {
/**
* @constructor
* @property {object} handlers The attached event handlers
* @property {object} data The JSON object that represents the tree structure
* @property {Element} node The DOM element to render the tree in
*/
constructor(data, node) {
this.handlers = {};
this.node = node;
this.data = data;
this.render();
}
/**
* Renders the tree view in the DOM
*/
render = () => {
const container = this.node;
container.innerHTML = "";
this.data.forEach((item) => container.appendChild(this.renderNode(item)));
[...container.querySelectorAll(".tree-leaf-text,.tree-expando")].forEach(
(node) => node.addEventListener("click", this.handleClickEvent),
);
};
renderNode = (item) => {
const leaf = document.createElement("div");
const content = document.createElement("div");
const text = document.createElement("div");
const expando = document.createElement("div");
leaf.setAttribute("class", "tree-leaf");
content.setAttribute("class", "tree-leaf-content");
text.setAttribute("class", "tree-leaf-text");
const { children, name, expanded } = item;
text.textContent = name;
expando.setAttribute("class", `tree-expando ${expanded ? "expanded" : ""}`);
expando.textContent = expanded ? "-" : "+";
content.appendChild(expando);
content.appendChild(text);
leaf.appendChild(content);
if (children?.length > 0) {
const childrenDiv = document.createElement("div");
childrenDiv.setAttribute("class", "tree-child-leaves");
children.forEach((child) => {
childrenDiv.appendChild(this.renderNode(child));
});
if (!expanded) {
childrenDiv.classList.add("hidden");
}
leaf.appendChild(childrenDiv);
} else {
expando.classList.add("hidden");
content.setAttribute("data-item", JSON.stringify(item));
}
return leaf;
};
handleClickEvent = (event) => {
const parent = (event.target || event.currentTarget).parentNode;
const leaves = parent.parentNode.querySelector(".tree-child-leaves");
if (leaves) {
this.setSubtreeVisibility(
parent,
leaves,
leaves.classList.contains("hidden"),
);
} else {
this.emit("select", {
target: event,
data: JSON.parse(parent.getAttribute("data-item")),
});
}
};
/**
* Expands/collapses by the expando or the leaf text
* @param {Element} node The parent node that contains the leaves
* @param {Element} leaves The leaves wrapper element
* @param {boolean} visible Expand or collapse?
* @param {boolean} skipEmit Skip emitting the event?
*/
setSubtreeVisibility(node, leaves, visible, skipEmit = false) {
leaves.classList.toggle("hidden", !visible);
node.querySelector(".tree-expando").textContent = visible ? "+" : "-";
if (skipEmit) {
return;
}
this.emit(visible ? "expand" : "collapse", {
target: node,
leaves,
});
}
on(name, callback, context = null) {
const handlers = this.handlers[name] || [];
handlers.push({ callback, context });
this.handlers[name] = handlers;
}
off(name, callback) {
this.handlers[name] = (this.handlers[name] || []).filter(
(handle) => handle.callback !== callback,
);
}
emit(name, ...args) {
(this.handlers[name] || []).forEach((handle) => {
window.setTimeout(() => {
handle.callback.apply(handle.context, args);
}, 0);
});
}
}
class SDDP_UI {
constructor() {
this.helpTextsConfigured = false;
this.wildcardsLoaded = false;
this.messageReadTimer = null;
this.lastMessage = null;
this.treeView = null;
}
configureHelpTexts() {
if (this.helpTextsConfigured) {
return;
}
// eslint-disable-next-line guard-for-in,no-restricted-syntax
for (const elemId in SDDP_HELP_TEXTS) {
const elem = gradioApp().getElementById(elemId);
if (elem) {
elem.setAttribute("title", SDDP_HELP_TEXTS[elemId]);
} else {
return; // Didn't find all elements...
}
}
this.helpTextsConfigured = true;
}
getInboxMessageText() {
return gradioApp().querySelector(
"#sddp-wildcard-s2c-message-textbox textarea",
)?.value;
}
formatPayload(payload) {
return JSON.stringify({ ...payload, id: Math.floor(+new Date()) }, null, 2);
}
sendAction(payload) {
const outbox = gradioApp().querySelector(
"#sddp-wildcard-c2s-message-textbox textarea",
);
outbox.value = this.formatPayload(payload);
// See https://github.com/AUTOMATIC1111/stable-diffusion-webui/commit/38b7186e6e3a4dffc93225308b822f0dae43a47d
window.updateInput?.(outbox);
gradioApp().querySelector("#sddp-wildcard-c2s-action-button").click();
}
requestWildcardTree() {
gradioApp().querySelector("#sddp-wildcard-load-tree-button")?.click();
}
doReadMessage() {
const messageText = this.getInboxMessageText();
if (!messageText || this.lastMessage === messageText) {
return;
}
this.lastMessage = messageText;
const message = JSON.parse(messageText);
const { action, success } = message;
if (action === "load tree" && success) {
this.setupTree(message.tree);
} else if (action === "load file" && success) {
this.loadFileIntoEditor(message);
} else {
console.warn("SDDP: Unknown message", message);
}
}
setupTree(content) {
let { treeView } = this;
if (!this.treeView) {
const treeDiv = gradioApp().querySelector("#sddp-wildcard-tree");
if (treeDiv) {
treeView = new SDDPTreeView(content, treeDiv);
treeView.on("select", this.onSelectNode.bind(this), null);
this.treeView = treeView;
}
} else {
treeView.data = content;
treeView.render();
}
}
onSelectNode(node) {
if (node.data?.name) {
this.sendAction({
action: "load file",
name: node.data.name,
});
}
}
loadFileIntoEditor(message) {
const editor = gradioApp().querySelector(
"#sddp-wildcard-file-editor textarea",
);
const name = gradioApp().querySelector("#sddp-wildcard-file-name textarea");
const saveButton = gradioApp().querySelector("#sddp-wildcard-save-button");
const { contents, wrapped_name: wrappedName, can_edit: canEdit } = message;
editor.value = contents;
name.value = wrappedName;
editor.readOnly = !canEdit;
saveButton.disabled = !canEdit;
// See https://github.com/AUTOMATIC1111/stable-diffusion-webui/commit/38b7186e6e3a4dffc93225308b822f0dae43a47d
window.updateInput?.(editor);
window.updateInput?.(name);
}
onWildcardManagerTabActivate() {
if (!this.wildcardsLoaded) {
this.requestWildcardTree();
this.wildcardsLoaded = true;
}
if (!this.messageReadTimer) {
this.messageReadTimer = setInterval(this.doReadMessage.bind(this), 120);
}
}
onDeleteTreeClick() {
// eslint-disable-next-line no-restricted-globals,no-alert
const sure = confirm("Are you sure you want to delete all your wildcards?");
return this.formatPayload({ action: "delete tree", sure });
}
onSaveFileClick() {
const json = JSON.parse(this.getInboxMessageText());
const contents = gradioApp().querySelector(
"#sddp-wildcard-file-editor textarea",
).value;
return this.formatPayload({
action: "save wildcard",
wildcard: json,
contents,
});
}
}
const SDDP = new SDDP_UI();
window.SDDP = SDDP;
onUiUpdate(() => {
SDDP.configureHelpTexts();
});
onUiLoaded(() => {
// TODO: would be nicer to use `onUiTabChange`, but it may be broken
const mutationObserver = new MutationObserver(() => {
if (get_uiCurrentTabContent()?.id === "tab_sddp-wildcard-manager") {
SDDP.onWildcardManagerTabActivate();
}
});
mutationObserver.observe(gradioApp(), { childList: true, subtree: true });
});