Merge pull request #3102 from ceruleandeep/fix/sdInteractiveYour Fix "send a photo of yourself" etc in SD interactive mode

f6d0f7587c21abee43bfa401ba753e4d2f51e9aa

Cohee <18619528+Cohee1207@users.noreply.github.com>

Signed
1 files changed, +80 -69Showing whitespace changes
public/scripts/extensions/stable-diffusion/index.js+80 -69
@@ -1,37 +1,62 @@
1import { Popper } from '../../../lib.js';1import { Popper } from '../../../lib.js';
2import {2import {
3 saveSettingsDebounced,3 animation_duration,
4 systemUserName,4 appendMediaToMessage,
5 getRequestHeaders,
6 event_types,5 event_types,
7 eventSource,6 eventSource,
7 formatCharacterAvatar,
8 generateQuietPrompt,8 generateQuietPrompt,
9 this_chid,9 getCharacterAvatar,
10 getCurrentChatId,10 getCurrentChatId,
11 animation_duration,11 getRequestHeaders,
12 appendMediaToMessage,
13 getUserAvatar,12 getUserAvatar,
14 user_avatar,13 saveSettingsDebounced,
15 getCharacterAvatar,
16 formatCharacterAvatar,
17 substituteParams,14 substituteParams,
18 substituteParamsExtended,15 substituteParamsExtended,
16 systemUserName,
17 this_chid,
18 user_avatar,
19} from '../../../script.js';19} from '../../../script.js';
20import { getApiUrl, getContext, extension_settings, doExtrasFetch, modules, renderExtensionTemplateAsync, writeExtensionField } from '../../extensions.js';20import {
21 doExtrasFetch,
22 extension_settings,
23 getApiUrl,
24 getContext,
25 modules,
26 renderExtensionTemplateAsync,
27 writeExtensionField,
28} from '../../extensions.js';
21import { selected_group } from '../../group-chats.js';29import { selected_group } from '../../group-chats.js';
22import { stringFormat, initScrollHeight, resetScrollHeight, getCharaFilename, saveBase64AsFile, getBase64Async, delay, isTrueBoolean, debounce, isFalseBoolean, deepMerge } from '../../utils.js';30import {
31 debounce,
32 deepMerge,
33 delay,
34 getBase64Async,
35 getCharaFilename,
36 initScrollHeight,
37 isFalseBoolean,
38 isTrueBoolean,
39 resetScrollHeight,
40 saveBase64AsFile,
41 stringFormat,
42} from '../../utils.js';
23import { getMessageTimeStamp, humanizedDateTime } from '../../RossAscends-mods.js';43import { getMessageTimeStamp, humanizedDateTime } from '../../RossAscends-mods.js';
24import { SECRET_KEYS, secret_state, writeSecret } from '../../secrets.js';44import { SECRET_KEYS, secret_state, writeSecret } from '../../secrets.js';
25import { getNovelUnlimitedImageGeneration, getNovelAnlas, loadNovelSubscriptionData } from '../../nai-settings.js';45import { getNovelAnlas, getNovelUnlimitedImageGeneration, loadNovelSubscriptionData } from '../../nai-settings.js';
26import { getMultimodalCaption } from '../shared.js';46import { getMultimodalCaption } from '../shared.js';
27import { SlashCommandParser } from '../../slash-commands/SlashCommandParser.js';47import { SlashCommandParser } from '../../slash-commands/SlashCommandParser.js';
28import { SlashCommand } from '../../slash-commands/SlashCommand.js';48import { SlashCommand } from '../../slash-commands/SlashCommand.js';
29import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from '../../slash-commands/SlashCommandArgument.js';49import {
50 ARGUMENT_TYPE,
51 SlashCommandArgument,
52 SlashCommandNamedArgument,
53} from '../../slash-commands/SlashCommandArgument.js';
30import { debounce_timeout } from '../../constants.js';54import { debounce_timeout } from '../../constants.js';
31import { SlashCommandEnumValue } from '../../slash-commands/SlashCommandEnumValue.js';55import { SlashCommandEnumValue } from '../../slash-commands/SlashCommandEnumValue.js';
32import { POPUP_RESULT, POPUP_TYPE, Popup, callGenericPopup } from '../../popup.js';56import { callGenericPopup, Popup, POPUP_RESULT, POPUP_TYPE } from '../../popup.js';
33import { commonEnumProviders } from '../../slash-commands/SlashCommandCommonEnumsProvider.js';57import { commonEnumProviders } from '../../slash-commands/SlashCommandCommonEnumsProvider.js';
34import { ToolManager } from '../../tool-calling.js';58import { ToolManager } from '../../tool-calling.js';
59
35export { MODULE_NAME };60export { MODULE_NAME };
3661
37const MODULE_NAME = 'sd';62const MODULE_NAME = 'sd';
@@ -118,13 +143,13 @@ const triggerWords = {
118};143};
119144
120const messageTrigger = {145const messageTrigger = {
121 activationRegex: /\b(send|mail|imagine|generate|make|create|draw|paint|render|show)\b.{0,10}\b(pic|picture|image|drawing|painting|photo|photograph)\b(?:\s+of)?(?:\s+(?:a|an|the|this|that|those|your)?)?(.+)/i,146 activationRegex: /\b(send|mail|imagine|generate|make|create|draw|paint|render|show)\b.{0,10}\b(pic|picture|image|drawing|painting|photo|photograph)\b(?:\s+of)?(?:\s+(?:a|an|the|this|that|those|your)?\s+)?(.+)/i,
122 specialCases: {147 specialCases: {
123 [generationMode.CHARACTER]: ['you', 'yourself'],148 [generationMode.CHARACTER]: ['you', 'yourself'],
124 [generationMode.USER]: ['me', 'myself'],149 [generationMode.USER]: ['me', 'myself'],
125 [generationMode.SCENARIO]: ['story', 'scenario', 'whole story'],150 [generationMode.SCENARIO]: ['story', 'scenario', 'whole story'],
126 [generationMode.NOW]: ['last message'],151 [generationMode.NOW]: ['last message'],
127 [generationMode.FACE]: ['your face', 'your portrait', 'your selfie'],152 [generationMode.FACE]: ['face', 'portrait', 'selfie'],
128 [generationMode.BACKGROUND]: ['background', 'scene background', 'scene', 'scenery', 'surroundings', 'environment'],153 [generationMode.BACKGROUND]: ['background', 'scene background', 'scene', 'scenery', 'surroundings', 'environment'],
129 },154 },
130};155};
@@ -343,7 +368,7 @@ function processTriggers(chat, _, abort) {
343 return;368 return;
344 }369 }
345370
346 console.log(`SD: Triggered by "${message}", detected subject: ${subject}"`);371 console.log(`SD: Triggered by "${message}", detected subject: "${subject}"`);
347372
348 outer: for (const [specialMode, triggers] of Object.entries(messageTrigger.specialCases)) {373 outer: for (const [specialMode, triggers] of Object.entries(messageTrigger.specialCases)) {
349 for (const trigger of triggers) {374 for (const trigger of triggers) {
@@ -359,7 +384,6 @@ function processTriggers(chat, _, abort) {
359 setTimeout(() => generatePicture(initiators.interactive, {}, subject, message), 1);384 setTimeout(() => generatePicture(initiators.interactive, {}, subject, message), 1);
360 } catch {385 } catch {
361 console.log('SD: Failed to process triggers.');386 console.log('SD: Failed to process triggers.');
362 return;
363 }387 }
364}388}
365389
@@ -685,7 +709,7 @@ async function refinePrompt(prompt, isNegative) {
685 return prompt;709 return prompt;
686}710}
687711
688function onChatChanged() {712async function onChatChanged() {
689 if (this_chid === undefined || selected_group) {713 if (this_chid === undefined || selected_group) {
690 $('#sd_character_prompt_block').hide();714 $('#sd_character_prompt_block').hide();
691 return;715 return;
@@ -713,7 +737,7 @@ function onChatChanged() {
713 $('#sd_character_prompt').val(characterPrompt);737 $('#sd_character_prompt').val(characterPrompt);
714 $('#sd_character_negative_prompt').val(negativePrompt);738 $('#sd_character_negative_prompt').val(negativePrompt);
715 $('#sd_character_prompt_share').prop('checked', hasSharedData);739 $('#sd_character_prompt_share').prop('checked', hasSharedData);
716 adjustElementScrollHeight();740 await adjustElementScrollHeight();
717}741}
718742
719async function adjustElementScrollHeight() {743async function adjustElementScrollHeight() {
@@ -1285,8 +1309,7 @@ async function getAutoRemoteModel() {
1285 throw new Error('SD WebUI returned an error.');1309 throw new Error('SD WebUI returned an error.');
1286 }1310 }
12871311
1288 const data = await result.text();1312 return await result.text();
1289 return data;
1290 } catch (error) {1313 } catch (error) {
1291 console.error(error);1314 console.error(error);
1292 return null;1315 return null;
@@ -1305,9 +1328,7 @@ async function getDrawthingsRemoteModel() {
1305 throw new Error('SD DrawThings API returned an error.');1328 throw new Error('SD DrawThings API returned an error.');
1306 }1329 }
13071330
1308 const data = await result.text();1331 return await result.text();
1309
1310 return data;
1311 } catch (error) {1332 } catch (error) {
1312 console.error(error);1333 console.error(error);
1313 return null;1334 return null;
@@ -1330,8 +1351,7 @@ async function getAutoRemoteUpscalers() {
1330 throw new Error('SD WebUI returned an error.');1351 throw new Error('SD WebUI returned an error.');
1331 }1352 }
13321353
1333 const data = await result.json();1354 return await result.json();
1334 return data;
1335 } catch (error) {1355 } catch (error) {
1336 console.error(error);1356 console.error(error);
1337 return [extension_settings.sd.hr_upscaler];1357 return [extension_settings.sd.hr_upscaler];
@@ -1350,8 +1370,7 @@ async function getAutoRemoteSchedulers() {
1350 throw new Error('SD WebUI returned an error.');1370 throw new Error('SD WebUI returned an error.');
1351 }1371 }
13521372
1353 const data = await result.json();1373 return await result.json();
1354 return data;
1355 } catch (error) {1374 } catch (error) {
1356 console.error(error);1375 console.error(error);
1357 return ['N/A'];1376 return ['N/A'];
@@ -1370,8 +1389,7 @@ async function getVladRemoteUpscalers() {
1370 throw new Error('SD.Next returned an error.');1389 throw new Error('SD.Next returned an error.');
1371 }1390 }
13721391
1373 const data = await result.json();1392 return await result.json();
1374 return data;
1375 } catch (error) {1393 } catch (error) {
1376 console.error(error);1394 console.error(error);
1377 return [extension_settings.sd.hr_upscaler];1395 return [extension_settings.sd.hr_upscaler];
@@ -1504,8 +1522,7 @@ async function loadHordeSamplers() {
1504 });1522 });
15051523
1506 if (result.ok) {1524 if (result.ok) {
1507 const data = await result.json();1525 return await result.json();
1508 return data;
1509 }1526 }
15101527
1511 return [];1528 return [];
@@ -1544,8 +1561,7 @@ async function loadAutoSamplers() {
1544 throw new Error('SD WebUI returned an error.');1561 throw new Error('SD WebUI returned an error.');
1545 }1562 }
15461563
1547 const data = await result.json();1564 return await result.json();
1548 return data;
1549 } catch (error) {1565 } catch (error) {
1550 return [];1566 return [];
1551 }1567 }
@@ -1583,8 +1599,7 @@ async function loadVladSamplers() {
1583 throw new Error('SD.Next returned an error.');1599 throw new Error('SD.Next returned an error.');
1584 }1600 }
15851601
1586 const data = await result.json();1602 return await result.json();
1587 return data;
1588 } catch (error) {1603 } catch (error) {
1589 return [];1604 return [];
1590 }1605 }
@@ -1723,8 +1738,7 @@ async function loadPollinationsModels() {
1723 });1738 });
17241739
1725 if (result.ok) {1740 if (result.ok) {
1726 const data = await result.json();1741 return await result.json();
1727 return data;
1728 }1742 }
17291743
1730 return [];1744 return [];
@@ -1742,8 +1756,7 @@ async function loadTogetherAIModels() {
1742 });1756 });
17431757
1744 if (result.ok) {1758 if (result.ok) {
1745 const data = await result.json();1759 return await result.json();
1746 return data;
1747 }1760 }
17481761
1749 return [];1762 return [];
@@ -1781,8 +1794,7 @@ async function loadNanoGPTModels() {
1781 });1794 });
17821795
1783 if (result.ok) {1796 if (result.ok) {
1784 const data = await result.json();1797 return await result.json();
1785 return data;
1786 }1798 }
17871799
1788 return [];1800 return [];
@@ -1798,8 +1810,10 @@ async function loadHordeModels() {
1798 if (result.ok) {1810 if (result.ok) {
1799 const data = await result.json();1811 const data = await result.json();
1800 data.sort((a, b) => b.count - a.count);1812 data.sort((a, b) => b.count - a.count);
1801 const models = data.map(x => ({ value: x.name, text: `${x.name} (ETA: ${x.eta}s, Queue: ${x.queued}, Workers: ${x.count})` }));1813 return data.map(x => ({
1802 return models;1814 value: x.name,
1815 text: `${x.name} (ETA: ${x.eta}s, Queue: ${x.queued}, Workers: ${x.count})`,
1816 }));
1803 }1817 }
18041818
1805 return [];1819 return [];
@@ -1824,8 +1838,7 @@ async function loadExtrasModels() {
18241838
1825 if (getModelsResult.ok) {1839 if (getModelsResult.ok) {
1826 const data = await getModelsResult.json();1840 const data = await getModelsResult.json();
1827 const view_models = data.models.map(x => ({ value: x, text: x }));1841 return data.models.map(x => ({ value: x, text: x }));
1828 return view_models;
1829 }1842 }
18301843
1831 return [];1844 return [];
@@ -1867,8 +1880,7 @@ async function loadAutoModels() {
1867 }1880 }
1868 }1881 }
18691882
1870 const data = await result.json();1883 return await result.json();
1871 return data;
1872 } catch (error) {1884 } catch (error) {
1873 return [];1885 return [];
1874 }1886 }
@@ -1953,8 +1965,7 @@ async function loadVladModels() {
1953 }1965 }
1954 }1966 }
19551967
1956 const data = await result.json();1968 return await result.json();
1957 return data;
1958 } catch (error) {1969 } catch (error) {
1959 return [];1970 return [];
1960 }1971 }
@@ -2242,7 +2253,7 @@ async function loadComfyWorkflows() {
2242 $('#sd_comfy_workflow').append(option);2253 $('#sd_comfy_workflow').append(option);
2243 }2254 }
2244 } catch (error) {2255 } catch (error) {
2245 return;2256 console.error(`Could not load ComfyUI workflows: ${error.message}`);
2246 }2257 }
2247}2258}
22482259
@@ -2361,7 +2372,7 @@ function ensureSelectionExists(setting, selector) {
2361 * @param {string} trigger Subject trigger word2372 * @param {string} trigger Subject trigger word
2362 * @param {string} [message] Chat message2373 * @param {string} [message] Chat message
2363 * @param {function} [callback] Callback function2374 * @param {function} [callback] Callback function
2364 * @returns {Promise<string>} Image path2375 * @returns {Promise<string|undefined>} Image path
2365 */2376 */
2366async function generatePicture(initiator, args, trigger, message, callback) {2377async function generatePicture(initiator, args, trigger, message, callback) {
2367 if (!trigger || trigger.trim().length === 0) {2378 if (!trigger || trigger.trim().length === 0) {
@@ -2379,7 +2390,9 @@ async function generatePicture(initiator, args, trigger, message, callback) {
23792390
2380 trigger = trigger.trim();2391 trigger = trigger.trim();
2381 const generationType = getGenerationType(trigger);2392 const generationType = getGenerationType(trigger);
2382 console.log('Generation mode', generationType, 'triggered with', trigger);2393 const generationTypeKey = Object.keys(generationMode).find(key => generationMode[key] === generationType);
2394 console.log(`Generation mode ${generationTypeKey} triggered with "${trigger}"`);
2395
2383 const quietPrompt = getQuietPrompt(generationType, trigger);2396 const quietPrompt = getQuietPrompt(generationType, trigger);
2384 const context = getContext();2397 const context = getContext();
23852398
@@ -2387,11 +2400,11 @@ async function generatePicture(initiator, args, trigger, message, callback) {
2387 ? context.groups[Object.keys(context.groups).filter(x => context.groups[x].id === context.groupId)[0]]?.id?.toString()2400 ? context.groups[Object.keys(context.groups).filter(x => context.groups[x].id === context.groupId)[0]]?.id?.toString()
2388 : context.characters[context.characterId]?.name;2401 : context.characters[context.characterId]?.name;
23892402
2390 if (generationType == generationMode.BACKGROUND) {2403 if (generationType === generationMode.BACKGROUND) {
2391 const callbackOriginal = callback;2404 const callbackOriginal = callback;
2392 callback = async function (prompt, imagePath, generationType, _negativePromptPrefix, _initiator, prefixedPrompt) {2405 callback = async function (prompt, imagePath, generationType, _negativePromptPrefix, _initiator, prefixedPrompt) {
2393 const imgUrl = `url("${encodeURI(imagePath)}")`;2406 const imgUrl = `url("${encodeURI(imagePath)}")`;
2394 eventSource.emit(event_types.FORCE_SET_BACKGROUND, { url: imgUrl, path: imagePath });2407 await eventSource.emit(event_types.FORCE_SET_BACKGROUND, { url: imgUrl, path: imagePath });
23952408
2396 if (typeof callbackOriginal === 'function') {2409 if (typeof callbackOriginal === 'function') {
2397 await callbackOriginal(prompt, imagePath, generationType, negativePromptPrefix, initiator, prefixedPrompt);2410 await callbackOriginal(prompt, imagePath, generationType, negativePromptPrefix, initiator, prefixedPrompt);
@@ -2446,12 +2459,12 @@ function setTypeSpecificDimensions(generationType) {
2446 const aspectRatio = extension_settings.sd.width / extension_settings.sd.height;2459 const aspectRatio = extension_settings.sd.width / extension_settings.sd.height;
24472460
2448 // Face images are always portrait (pun intended)2461 // Face images are always portrait (pun intended)
2449 if ((generationType == generationMode.FACE || generationType == generationMode.FACE_MULTIMODAL) && aspectRatio >= 1) {2462 if ((generationType === generationMode.FACE || generationType === generationMode.FACE_MULTIMODAL) && aspectRatio >= 1) {
2450 // Round to nearest multiple of 642463 // Round to nearest multiple of 64
2451 extension_settings.sd.height = Math.round(extension_settings.sd.width * 1.5 / 64) * 64;2464 extension_settings.sd.height = Math.round(extension_settings.sd.width * 1.5 / 64) * 64;
2452 }2465 }
24532466
2454 if (generationType == generationMode.BACKGROUND) {2467 if (generationType === generationMode.BACKGROUND) {
2455 // Background images are always landscape2468 // Background images are always landscape
2456 if (aspectRatio <= 1) {2469 if (aspectRatio <= 1) {
2457 // Round to nearest multiple of 642470 // Round to nearest multiple of 64
@@ -2537,7 +2550,7 @@ async function getPrompt(generationType, message, trigger, quietPrompt, combineN
2537 */2550 */
2538function generateFreeModePrompt(trigger, combineNegatives) {2551function generateFreeModePrompt(trigger, combineNegatives) {
2539 return trigger2552 return trigger
2540 .replace(/(?:^char(\s|,)|\{\{charPrefix\}\})/gi, (_, suffix) => {2553 .replace(/^char(\s|,)|{{charPrefix}}/gi, (_, suffix) => {
2541 const getLastCharacterKey = () => {2554 const getLastCharacterKey = () => {
2542 if (typeof this_chid !== 'undefined') {2555 if (typeof this_chid !== 'undefined') {
2543 return getCharaFilename(this_chid);2556 return getCharaFilename(this_chid);
@@ -2545,9 +2558,7 @@ function generateFreeModePrompt(trigger, combineNegatives) {
2545 const context = getContext();2558 const context = getContext();
2546 for (let i = context.chat.length - 1; i >= 0; i--) {2559 for (let i = context.chat.length - 1; i >= 0; i--) {
2547 const message = context.chat[i];2560 const message = context.chat[i];
2548 if (message.is_user || message.is_system) {2561 if (!message.is_user && !message.is_system && typeof message.original_avatar === 'string') {
2549 continue;
2550 } else if (typeof message.original_avatar === 'string') {
2551 return message.original_avatar.replace(/\.[^/.]+$/, '');2562 return message.original_avatar.replace(/\.[^/.]+$/, '');
2552 }2563 }
2553 }2564 }
@@ -2570,11 +2581,11 @@ function generateFreeModePrompt(trigger, combineNegatives) {
2570async function generateMultimodalPrompt(generationType, quietPrompt) {2581async function generateMultimodalPrompt(generationType, quietPrompt) {
2571 let avatarUrl;2582 let avatarUrl;
25722583
2573 if (generationType == generationMode.USER_MULTIMODAL) {2584 if (generationType === generationMode.USER_MULTIMODAL) {
2574 avatarUrl = getUserAvatarUrl();2585 avatarUrl = getUserAvatarUrl();
2575 }2586 }
25762587
2577 if (generationType == generationMode.CHARACTER_MULTIMODAL || generationType === generationMode.FACE_MULTIMODAL) {2588 if (generationType === generationMode.CHARACTER_MULTIMODAL || generationType === generationMode.FACE_MULTIMODAL) {
2578 avatarUrl = getCharacterAvatarUrl();2589 avatarUrl = getCharacterAvatarUrl();
2579 }2590 }
25802591
@@ -3182,8 +3193,8 @@ function getNovelParams() {
3182 const ratio = Math.sqrt(MAX_PIXELS / (width * height));3193 const ratio = Math.sqrt(MAX_PIXELS / (width * height));
31833194
3184 // Calculate new width and height while maintaining aspect ratio.3195 // Calculate new width and height while maintaining aspect ratio.
3185 var newWidth = Math.round(width * ratio);3196 let newWidth = Math.round(width * ratio);
3186 var newHeight = Math.round(height * ratio);3197 let newHeight = Math.round(height * ratio);
31873198
3188 // Ensure new dimensions are multiples of 64. If not, reduce accordingly.3199 // Ensure new dimensions are multiples of 64. If not, reduce accordingly.
3189 if (newWidth % 64 !== 0) {3200 if (newWidth % 64 !== 0) {
@@ -3479,9 +3490,9 @@ async function onComfyOpenWorkflowEditorClick() {
3479 const popupResult = popup.show();3490 const popupResult = popup.show();
3480 const checkPlaceholders = () => {3491 const checkPlaceholders = () => {
3481 workflow = $('#sd_comfy_workflow_editor_workflow').val().toString();3492 workflow = $('#sd_comfy_workflow_editor_workflow').val().toString();
3482 $('.sd_comfy_workflow_editor_placeholder_list > li[data-placeholder]').each(function (idx) {3493 $('.sd_comfy_workflow_editor_placeholder_list > li[data-placeholder]').each(function () {
3483 const key = this.getAttribute('data-placeholder');3494 const key = this.getAttribute('data-placeholder');
3484 const found = workflow.search(`"%${key}%"`) != -1;3495 const found = workflow.search(`"%${key}%"`) !== -1;
3485 this.classList[found ? 'remove' : 'add']('sd_comfy_workflow_editor_not_found');3496 this.classList[found ? 'remove' : 'add']('sd_comfy_workflow_editor_not_found');
3486 });3497 });
3487 };3498 };
@@ -3841,7 +3852,7 @@ async function sdMessageButton(e) {
3841 swipes.push(image);3852 swipes.push(image);
38423853
3843 // If already contains an image and it's not inline - leave it as is3854 // If already contains an image and it's not inline - leave it as is
3844 message.extra.inline_image = message.extra.image && !message.extra.inline_image ? false : true;3855 message.extra.inline_image = !(message.extra.image && !message.extra.inline_image);
3845 message.extra.image = image;3856 message.extra.image = image;
3846 message.extra.title = prompt;3857 message.extra.title = prompt;
3847 message.extra.generationType = generationType;3858 message.extra.generationType = generationType;