From 92ed011876ff1b121f6cbd68c376b7bb4176632f Mon Sep 17 00:00:00 2001 From: Svornost <11434-svornost@users.noreply.gitgud.io> Date: Mon, 9 Oct 2023 22:25:04 -0400 Subject: [PATCH] Add per-slave prompt customization to the Customize tab in Slave Interact. --- src/art/genAI/buildPrompt.js | 1 + src/art/genAI/posturePromptPart.js | 4 ++ src/interaction/siCustom.js | 98 +++++++++++++++++++++++++++++- src/js/SlaveState.js | 19 ++++++ 4 files changed, 120 insertions(+), 2 deletions(-) diff --git a/src/art/genAI/buildPrompt.js b/src/art/genAI/buildPrompt.js index a1015ba089f..5e6fff9992e 100644 --- a/src/art/genAI/buildPrompt.js +++ b/src/art/genAI/buildPrompt.js @@ -30,6 +30,7 @@ function buildPrompt(slave) { new App.Art.GenAI.PiercingsPromptPart(slave), new App.Art.GenAI.HealthPromptPart(slave), new App.Art.GenAI.PubicHairPromptPart(slave), + new App.Art.GenAI.CustomPromptPart(slave), ]; return new App.Art.GenAI.Prompt(prompts); } diff --git a/src/art/genAI/posturePromptPart.js b/src/art/genAI/posturePromptPart.js index 9653828fe56..4ec86fef09a 100644 --- a/src/art/genAI/posturePromptPart.js +++ b/src/art/genAI/posturePromptPart.js @@ -3,6 +3,10 @@ App.Art.GenAI.PosturePromptPart = class PosturePromptPart extends App.Art.GenAI. * @returns {string} */ positive() { + if (this.slave.custom.aiPrompts?.pose) { + return this.slave.custom.aiPrompts.pose; + } + let devotionPart; if (this.slave.devotion < -50) { devotionPart = `standing, from side, arms crossed`; diff --git a/src/interaction/siCustom.js b/src/interaction/siCustom.js index 21fb5d5ff2e..7f2dc02dd5b 100644 --- a/src/interaction/siCustom.js +++ b/src/interaction/siCustom.js @@ -18,6 +18,7 @@ App.UI.SlaveInteract.custom = function(slave, refresh) { customSlaveImage(), customHairImage(), artSeed(), + aiPrompts(), ); App.UI.DOM.appendNewElement("h3", el, `Names`); @@ -710,11 +711,104 @@ App.UI.SlaveInteract.custom = function(slave, refresh) { button )); } + return frag; + } + + function aiPrompts() { + function posePrompt() { + let el = document.createElement('p'); + el.append(`Override ${his} pose: `); + el.appendChild( + App.UI.DOM.makeTextBox( + slave.custom.aiPrompts.pose, + v => { + slave.custom.aiPrompts.pose = v; + $(promptDiv).empty().append(genAIPrompt()); + } + ) + ); + + let choices = document.createElement('div'); + choices.className = "choices"; + choices.appendChild(App.UI.DOM.makeElement('span', ` This prompt will replace the default body pose prompts. Example: 'kneeling, arms behind back'`, 'note')); + el.appendChild(choices); + return el; + } + + function positivePrompt() { + let el = document.createElement('p'); + el.append(`Add positive prompts: `); + el.appendChild( + App.UI.DOM.makeTextBox( + slave.custom.aiPrompts.positive, + v => { + slave.custom.aiPrompts.positive = v; + $(promptDiv).empty().append(genAIPrompt()); + } + ) + ); + + let choices = document.createElement('div'); + choices.className = "choices"; + choices.appendChild(App.UI.DOM.makeElement('span', ` Prompts specified here will be appended to the end of the dynamic positive prompt; specify things you want to see in the rendered image.`, 'note')); + el.appendChild(choices); + return el; + } + + function negativePrompt() { + let el = document.createElement('p'); + el.append(`Add negative prompts: `); + el.appendChild( + App.UI.DOM.makeTextBox( + slave.custom.aiPrompts.negative, + v => { + slave.custom.aiPrompts.negative = v; + $(promptDiv).empty().append(genAIPrompt()); + } + ) + ); + + let choices = document.createElement('div'); + choices.className = "choices"; + choices.appendChild(App.UI.DOM.makeElement('span', ` Prompts specified here will be appended to the end of the dynamic negative prompt; specify things you don't want to see in the rendered image.`, 'note')); + el.appendChild(choices); + return el; + } + + const frag = new DocumentFragment(); // Debug information for AI art, or prompt suggestions for custom images - if ((V.imageChoice === 6 && V.debugMode === 1) || (V.seeCustomImagesOnly && V.aiCustomImagePrompts)) { - frag.append(genAIPrompt()); + const promptDiv = App.UI.DOM.makeElement('div'); + if ((V.imageChoice === 6 && (V.debugMode === 1 || slave.custom.aiPrompts)) || (V.seeCustomImagesOnly && V.aiCustomImagePrompts)) { + promptDiv.append(genAIPrompt()); + } else if (V.imageChoice === 6) { + promptDiv.append(App.UI.DOM.link("Show AI Prompts", f => { + $(promptDiv).empty().append(genAIPrompt()); + })); + } + frag.append(promptDiv); + + // Custom prompt parts + const customDiv = App.UI.DOM.makeElement('div'); + if (V.imageChoice === 6 || (V.seeCustomImagesOnly && V.aiCustomImagePrompts)) { + if (slave.custom.aiPrompts) { + customDiv.append( + posePrompt(), + positivePrompt(), + negativePrompt(), + ); + customDiv.append(App.UI.DOM.link("Disable Prompt Customization", f => { + delete slave.custom.aiPrompts; + refresh(); + })); + } else { + customDiv.append(App.UI.DOM.link("Customize AI Prompts", f => { + slave.custom.aiPrompts = new App.Entity.SlaveCustomAIPrompts(); + refresh(); + })); + } } + frag.append(customDiv); return frag; } diff --git a/src/js/SlaveState.js b/src/js/SlaveState.js index e22a6d40131..1fbff2dd119 100644 --- a/src/js/SlaveState.js +++ b/src/js/SlaveState.js @@ -351,6 +351,20 @@ App.Entity.SlaveActionsCountersState = class { } }; +/** + * Encapsulates custom AI prompts + */ +App.Entity.SlaveCustomAIPrompts = class SlaveCustomAIPrompts { + constructor() { + /** replaces the slave's posture prompts with a custom string for user-specified poses */ + this.pose = ""; + /** manually adds to the dynamic positive prompt string */ + this.positive = ""; + /** manually adds to the dynamic negative prompt string */ + this.negative = ""; + } +}; + /** * Encapsulates various custom properties, set by users */ @@ -389,6 +403,11 @@ App.Entity.SlaveCustomAddonsState = class SlaveCustomAddonsState { * @type {number} */ this.aiImageId = null; + /** + * custom AI prompts; may be null or absent + * @type {App.Entity.SlaveCustomAIPrompts} + */ + this.aiPrompts = null; } }; -- GitLab