Merge branch 'staging' into model-write-in
| @@ -118,7 +118,6 @@ extras: | |||
| 118 | classificationModel: Cohee/distilbert-base-uncased-go-emotions-onnx | 118 | classificationModel: Cohee/distilbert-base-uncased-go-emotions-onnx |
| 119 | captioningModel: Xenova/vit-gpt2-image-captioning | 119 | captioningModel: Xenova/vit-gpt2-image-captioning |
| 120 | embeddingModel: Cohee/jina-embeddings-v2-base-en | 120 | embeddingModel: Cohee/jina-embeddings-v2-base-en |
| 121 | promptExpansionModel: Cohee/fooocus_expansion-onnx | ||
| 122 | speechToTextModel: Xenova/whisper-small | 121 | speechToTextModel: Xenova/whisper-small |
| 123 | textToSpeechModel: Xenova/speecht5_tts | 122 | textToSpeechModel: Xenova/speecht5_tts |
| 124 | # -- OPENAI CONFIGURATION -- | 123 | # -- OPENAI CONFIGURATION -- |
| @@ -1709,9 +1709,9 @@ | |||
| 1709 | <div class=""> | 1709 | <div class=""> |
| 1710 | <div class="inline-drawer wide100p flexFlowColumn"> | 1710 | <div class="inline-drawer wide100p flexFlowColumn"> |
| 1711 | <div class="inline-drawer-toggle inline-drawer-header"> | 1711 | <div class="inline-drawer-toggle inline-drawer-header"> |
| 1712 | <div class="flex-container alignItemsCenter flexNoGap"> | 1712 | <div class="flex-container alignItemsCenter"> |
| 1713 | <b data-i18n="Character Names Behavior">Character Names Behavior</b> | 1713 | <b data-i18n="Character Names Behavior">Character Names Behavior</b> |
| 1714 | <span title="Helps the model to associate messages with characters." data-i18n="[title]Helps the model to associate messages with characters." class="note-link-span fa-solid fa-circle-question"></span> | 1714 | <span title="Helps the model to associate messages with characters." data-i18n="[title]Helps the model to associate messages with characters." class="opacity50p fa-solid fa-circle-info"></span> |
| 1715 | <small class="flexBasis100p">(<span id="character_names_display"></span>)</small> | 1715 | <small class="flexBasis100p">(<span id="character_names_display"></span>)</small> |
| 1716 | </div> | 1716 | </div> |
| 1717 | <div class="fa-solid fa-circle-chevron-down inline-drawer-icon down"></div> | 1717 | <div class="fa-solid fa-circle-chevron-down inline-drawer-icon down"></div> |
| @@ -1720,25 +1720,22 @@ | |||
| 1720 | <label class="checkbox_label flexWrap alignItemsCenter" for="character_names_none"> | 1720 | <label class="checkbox_label flexWrap alignItemsCenter" for="character_names_none"> |
| 1721 | <input type="radio" id="character_names_none" name="character_names" value="-1"> | 1721 | <input type="radio" id="character_names_none" name="character_names" value="-1"> |
| 1722 | <span data-i18n="None">None</span> | 1722 | <span data-i18n="None">None</span> |
| 1723 | <i class="right_menu_button fa-solid fa-circle-exclamation" title="Never add character name prefixes. May behave poorly in groups, choose with caution." data-i18n="[title]character_names_none"></i> | 1723 | <small class="flexBasis100p" data-i18n="character_names_none"> |
| 1724 | <small class="flexBasis100p" data-i18n="Never add character names."> | 1724 | Never add character name prefixes. May behave poorly in groups, choose with caution. |
| 1725 | Never add character names. | ||
| 1726 | </small> | 1725 | </small> |
| 1727 | </label> | 1726 | </label> |
| 1728 | <label class="checkbox_label flexWrap alignItemsCenter" for="character_names_default"> | 1727 | <label class="checkbox_label flexWrap alignItemsCenter" for="character_names_default"> |
| 1729 | <input type="radio" id="character_names_default" name="character_names" value="0"> | 1728 | <input type="radio" id="character_names_default" name="character_names" value="0"> |
| 1730 | <span data-i18n="Default">Default</span> | 1729 | <span data-i18n="Default">Default</span> |
| 1731 | <i class="right_menu_button fa-solid fa-circle-exclamation" title="Add prefixes for groups and past personas. Otherwise, make sure you provide names in the prompt." data-i18n="[title]character_names_default"></i> | 1730 | <small class="flexBasis100p" data-i18n="character_names_default"> |
| 1732 | <small class="flexBasis100p" data-i18n="Don't add character names unless necessary."> | 1731 | Add prefixes for groups and past personas. Otherwise, make sure you provide names in the prompt. |
| 1733 | Don't add character names unless necessary. | ||
| 1734 | </small> | 1732 | </small> |
| 1735 | </label> | 1733 | </label> |
| 1736 | <label class="checkbox_label flexWrap alignItemsCenter" for="character_names_completion"> | 1734 | <label class="checkbox_label flexWrap alignItemsCenter" for="character_names_completion"> |
| 1737 | <input type="radio" id="character_names_completion" name="character_names" value="1"> | 1735 | <input type="radio" id="character_names_completion" name="character_names" value="1"> |
| 1738 | <span data-i18n="Completion Object">Completion Object</span> | 1736 | <span data-i18n="Completion Object">Completion Object</span> |
| 1739 | <i class="right_menu_button fa-solid fa-circle-exclamation" title="Restrictions apply: only Latin alphanumerics and underscores. Doesn't work for all sources, notably: Claude, MistralAI, Google." data-i18n="[title]character_names_completion"></i> | 1737 | <small class="flexBasis100p" data-i18n="character_names_completion"> |
| 1740 | <small class="flexBasis100p" data-i18n="Add character names to completion objects."> | 1738 | Add character names to completion objects. Restrictions apply: only Latin alphanumerics and underscores. |
| 1741 | Add character names to completion objects. | ||
| 1742 | </small> | 1739 | </small> |
| 1743 | </label> | 1740 | </label> |
| 1744 | <label class="checkbox_label flexWrap alignItemsCenter" for="character_names_content"> | 1741 | <label class="checkbox_label flexWrap alignItemsCenter" for="character_names_content"> |
| @@ -1754,9 +1751,9 @@ | |||
| 1754 | </div> | 1751 | </div> |
| 1755 | <div class="inline-drawer wide100p flexFlowColumn marginBot10"> | 1752 | <div class="inline-drawer wide100p flexFlowColumn marginBot10"> |
| 1756 | <div class="inline-drawer-toggle inline-drawer-header"> | 1753 | <div class="inline-drawer-toggle inline-drawer-header"> |
| 1757 | <div class="flex-container alignItemsCenter flexNoGap"> | 1754 | <div class="flex-container alignItemsCenter"> |
| 1758 | <b data-i18n="Continue Postfix">Continue Postfix</b> | 1755 | <b data-i18n="Continue Postfix">Continue Postfix</b> |
| 1759 | <span data-i18n="[title]The next chunk of the continued message will be appended using this as a separator." title="The next chunk of the continued message will be appended using this as a separator." class="note-link-span fa-solid fa-circle-question"></span> | 1756 | <span data-i18n="[title]The next chunk of the continued message will be appended using this as a separator." title="The next chunk of the continued message will be appended using this as a separator." class="opacity50p fa-solid fa-circle-info"></span> |
| 1760 | <small class="flexBasis100p">(<span id="continue_postfix_display"></span>)</small> | 1757 | <small class="flexBasis100p">(<span id="continue_postfix_display"></span>)</small> |
| 1761 | </div> | 1758 | </div> |
| 1762 | <div class="fa-solid fa-circle-chevron-down inline-drawer-icon down"></div> | 1759 | <div class="fa-solid fa-circle-chevron-down inline-drawer-icon down"></div> |
| @@ -4013,6 +4010,10 @@ | |||
| 4013 | <input id="compact_input_area" type="checkbox" /> | 4010 | <input id="compact_input_area" type="checkbox" /> |
| 4014 | <small data-i18n="Compact Input Area (Mobile)">Compact Input Area</small><i class="fa-solid fa-mobile-screen-button"></i> | 4011 | <small data-i18n="Compact Input Area (Mobile)">Compact Input Area</small><i class="fa-solid fa-mobile-screen-button"></i> |
| 4015 | </label> | 4012 | </label> |
| 4013 | <label for="show_swipe_num_all_messages" class="checkbox_label" title="Display swipe numbers for all messages, not just the last." data-i18n="[title]Display swipe numbers for all messages, not just the last."> | ||
| 4014 | <input id="show_swipe_num_all_messages" type="checkbox" /> | ||
| 4015 | <small data-i18n="Swipe # for All Messages">Swipe # for All Messages</small><i class="fa-solid fa-mobile-screen-button"></i> | ||
| 4016 | </label> | ||
| 4016 | <label for="hotswapEnabled" class="checkbox_label" title="In the Character Management panel, show quick selection buttons for favorited characters." data-i18n="[title]In the Character Management panel, show quick selection buttons for favorited characters"> | 4017 | <label for="hotswapEnabled" class="checkbox_label" title="In the Character Management panel, show quick selection buttons for favorited characters." data-i18n="[title]In the Character Management panel, show quick selection buttons for favorited characters"> |
| 4017 | <input id="hotswapEnabled" type="checkbox" /> | 4018 | <input id="hotswapEnabled" type="checkbox" /> |
| 4018 | <small data-i18n="Characters Hotswap">Characters Hotswap</small> | 4019 | <small data-i18n="Characters Hotswap">Characters Hotswap</small> |
| @@ -5941,9 +5942,10 @@ | |||
| 5941 | </div> | 5942 | </div> |
| 5942 | <div class="mes_bias"></div> | 5943 | <div class="mes_bias"></div> |
| 5943 | </div> | 5944 | </div> |
| 5944 | <div class="swipe_right fa-solid fa-chevron-right" style="display: none;"> | 5945 | <div class="flex-container swipeRightBlock flexFlowColumn flexNoGap"> |
| 5945 | <div class="swipes-counter"></div> | 5946 | <div class="swipe_right fa-solid fa-chevron-right" style="display: none;"></div> |
| 5946 | </div> | 5947 | <div class="swipes-counter"></div> |
| 5948 | </div> | ||
| 5947 | </div> | 5949 | </div> |
| 5948 | </div> | 5950 | </div> |
| 5949 | <div id="group_avatars_template" class="template_element"> | 5951 | <div id="group_avatars_template" class="template_element"> |
| @@ -83,6 +83,7 @@ import { | |||
| 83 | resetMovableStyles, | 83 | resetMovableStyles, |
| 84 | forceCharacterEditorTokenize, | 84 | forceCharacterEditorTokenize, |
| 85 | applyPowerUserSettings, | 85 | applyPowerUserSettings, |
| 86 | switchSwipeNumAllMessages, | ||
| 86 | } from './scripts/power-user.js'; | 87 | } from './scripts/power-user.js'; |
| 87 | 88 | ||
| 88 | import { | 89 | import { |
| @@ -2369,6 +2370,13 @@ export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll | |||
| 2369 | 2370 | ||
| 2370 | addCopyToCodeBlocks(newMessage); | 2371 | addCopyToCodeBlocks(newMessage); |
| 2371 | 2372 | ||
| 2373 | // Set the swipes counter for past messages, only visible if 'Show Swipes on All Message' is enabled | ||
| 2374 | if (!params.isUser && newMessageId !== 0 && newMessageId !== chat.length - 1) { | ||
| 2375 | const swipesNum = chat[newMessageId].swipes?.length; | ||
| 2376 | const swipeId = chat[newMessageId].swipe_id + 1; | ||
| 2377 | newMessage.find('.swipes-counter').text(`${swipeId}\u200B/\u200b${swipesNum}`); | ||
| 2378 | } | ||
| 2379 | |||
| 2372 | if (showSwipes) { | 2380 | if (showSwipes) { |
| 2373 | $('#chat .mes').last().addClass('last_mes'); | 2381 | $('#chat .mes').last().addClass('last_mes'); |
| 2374 | $('#chat .mes').eq(-2).removeClass('last_mes'); | 2382 | $('#chat .mes').eq(-2).removeClass('last_mes'); |
| @@ -4785,7 +4793,7 @@ export function removeMacros(str) { | |||
| 4785 | * @param {boolean} [compact] Send as a compact display message. | 4793 | * @param {boolean} [compact] Send as a compact display message. |
| 4786 | * @param {string} [name] Name of the user sending the message. Defaults to name1. | 4794 | * @param {string} [name] Name of the user sending the message. Defaults to name1. |
| 4787 | * @param {string} [avatar] Avatar of the user sending the message. Defaults to user_avatar. | 4795 | * @param {string} [avatar] Avatar of the user sending the message. Defaults to user_avatar. |
| 4788 | * @returns {Promise<void>} A promise that resolves when the message is inserted. | 4796 | * @returns {Promise<any>} A promise that resolves to the message when it is inserted. |
| 4789 | */ | 4797 | */ |
| 4790 | export async function sendMessageAsUser(messageText, messageBias, insertAt = null, compact = false, name = name1, avatar = user_avatar) { | 4798 | export async function sendMessageAsUser(messageText, messageBias, insertAt = null, compact = false, name = name1, avatar = user_avatar) { |
| 4791 | messageText = getRegexedString(messageText, regex_placement.USER_INPUT); | 4799 | messageText = getRegexedString(messageText, regex_placement.USER_INPUT); |
| @@ -4832,6 +4840,8 @@ export async function sendMessageAsUser(messageText, messageBias, insertAt = nul | |||
| 4832 | await eventSource.emit(event_types.USER_MESSAGE_RENDERED, chat_id); | 4840 | await eventSource.emit(event_types.USER_MESSAGE_RENDERED, chat_id); |
| 4833 | await saveChatConditional(); | 4841 | await saveChatConditional(); |
| 4834 | } | 4842 | } |
| 4843 | |||
| 4844 | return message; | ||
| 4835 | } | 4845 | } |
| 4836 | 4846 | ||
| 4837 | /** | 4847 | /** |
| @@ -7469,7 +7479,6 @@ export function showSwipeButtons() { | |||
| 7469 | //had to add this to make the swipe counter work | 7479 | //had to add this to make the swipe counter work |
| 7470 | //(copied from the onclick functions for swipe buttons.. | 7480 | //(copied from the onclick functions for swipe buttons.. |
| 7471 | //don't know why the array isn't set for non-swipe messsages in Generate or addOneMessage..) | 7481 | //don't know why the array isn't set for non-swipe messsages in Generate or addOneMessage..) |
| 7472 | |||
| 7473 | if (chat[chat.length - 1]['swipe_id'] === undefined) { // if there is no swipe-message in the last spot of the chat array | 7482 | if (chat[chat.length - 1]['swipe_id'] === undefined) { // if there is no swipe-message in the last spot of the chat array |
| 7474 | chat[chat.length - 1]['swipe_id'] = 0; // set it to id 0 | 7483 | chat[chat.length - 1]['swipe_id'] = 0; // set it to id 0 |
| 7475 | chat[chat.length - 1]['swipes'] = []; // empty the array | 7484 | chat[chat.length - 1]['swipes'] = []; // empty the array |
| @@ -7479,32 +7488,36 @@ export function showSwipeButtons() { | |||
| 7479 | const currentMessage = $('#chat').children().filter(`[mesid="${chat.length - 1}"]`); | 7488 | const currentMessage = $('#chat').children().filter(`[mesid="${chat.length - 1}"]`); |
| 7480 | const swipeId = chat[chat.length - 1].swipe_id; | 7489 | const swipeId = chat[chat.length - 1].swipe_id; |
| 7481 | const swipeCounterText = (`${(swipeId + 1)}\u200B/\u200b${(chat[chat.length - 1].swipes.length)}`); | 7490 | const swipeCounterText = (`${(swipeId + 1)}\u200B/\u200b${(chat[chat.length - 1].swipes.length)}`); |
| 7491 | const swipeRight = currentMessage.find('.swipe_right'); | ||
| 7492 | const swipeLeft = currentMessage.find('.swipe_left'); | ||
| 7493 | const swipeCounter = currentMessage.find('.swipes-counter'); | ||
| 7482 | 7494 | ||
| 7483 | if (swipeId !== undefined && (chat[chat.length - 1].swipes.length > 1 || swipeId > 0)) { | 7495 | if (swipeId !== undefined && (chat[chat.length - 1].swipes.length > 1 || swipeId > 0)) { |
| 7484 | currentMessage.children('.swipe_left').css('display', 'flex'); | 7496 | swipeLeft.css('display', 'flex'); |
| 7485 | } | 7497 | } |
| 7486 | //only show right when generate is off, or when next right swipe would not make a generate happen | 7498 | //only show right when generate is off, or when next right swipe would not make a generate happen |
| 7487 | if (is_send_press === false || chat[chat.length - 1].swipes.length >= swipeId) { | 7499 | if (is_send_press === false || chat[chat.length - 1].swipes.length >= swipeId) { |
| 7488 | currentMessage.children('.swipe_right').css('display', 'flex'); | 7500 | swipeRight.css('display', 'flex').css('opacity', '0.3'); |
| 7489 | currentMessage.children('.swipe_right').css('opacity', '0.3'); | 7501 | swipeCounter.css('opacity', '0.3'); |
| 7490 | } | 7502 | } |
| 7491 | //console.log((chat[chat.length - 1])); | ||
| 7492 | if ((chat[chat.length - 1].swipes.length - swipeId) === 1) { | 7503 | if ((chat[chat.length - 1].swipes.length - swipeId) === 1) { |
| 7493 | //console.log('highlighting R swipe'); | 7504 | //chevron was moved out of hardcode in HTML to class toggle dependent on last_mes or not |
| 7494 | currentMessage.children('.swipe_right').css('opacity', '0.7'); | 7505 | //necessary for 'swipe_right' div in past messages to have no chevron if 'show swipes for all messages' is turned on |
| 7506 | swipeRight.css('opacity', '0.7'); | ||
| 7507 | swipeCounter.css('opacity', '0.7'); | ||
| 7495 | } | 7508 | } |
| 7496 | //console.log(swipesCounterHTML); | ||
| 7497 | 7509 | ||
| 7498 | $('.swipes-counter').text(swipeCounterText); | 7510 | //allows for writing individual swipe counters for past messages |
| 7511 | const lastSwipeCounter = $('.last_mes .swipes-counter'); | ||
| 7512 | lastSwipeCounter.text(swipeCounterText).show(); | ||
| 7499 | 7513 | ||
| 7500 | //console.log(swipeId); | 7514 | switchSwipeNumAllMessages(); |
| 7501 | //console.log(chat[chat.length - 1].swipes.length); | ||
| 7502 | } | 7515 | } |
| 7503 | 7516 | ||
| 7504 | export function hideSwipeButtons() { | 7517 | export function hideSwipeButtons() { |
| 7505 | //console.log('hideswipebuttons entered'); | 7518 | $('#chat').find('.swipe_right').hide(); |
| 7506 | $('#chat').find('.swipe_right').css('display', 'none'); | 7519 | $('#chat').find('.last_mes .swipes-counter').hide(); |
| 7507 | $('#chat').find('.swipe_left').css('display', 'none'); | 7520 | $('#chat').find('.swipe_left').hide(); |
| 7508 | } | 7521 | } |
| 7509 | 7522 | ||
| 7510 | /** | 7523 | /** |
| @@ -8339,8 +8352,8 @@ const swipe_right = () => { | |||
| 8339 | } | 8352 | } |
| 8340 | 8353 | ||
| 8341 | const currentMessage = $('#chat').children().filter(`[mesid="${chat.length - 1}"]`); | 8354 | const currentMessage = $('#chat').children().filter(`[mesid="${chat.length - 1}"]`); |
| 8342 | let this_div = currentMessage.children('.swipe_right'); | 8355 | let this_div = currentMessage.find('.swipe_right'); |
| 8343 | let this_mes_div = this_div.parent(); | 8356 | let this_mes_div = this_div.parent().parent(); |
| 8344 | 8357 | ||
| 8345 | if (chat[chat.length - 1]['swipe_id'] > chat[chat.length - 1]['swipes'].length) { //if we swipe right while generating (the swipe ID is greater than what we are viewing now) | 8358 | if (chat[chat.length - 1]['swipe_id'] > chat[chat.length - 1]['swipes'].length) { //if we swipe right while generating (the swipe ID is greater than what we are viewing now) |
| 8346 | chat[chat.length - 1]['swipe_id'] = chat[chat.length - 1]['swipes'].length; //show that message slot (will be '...' while generating) | 8359 | chat[chat.length - 1]['swipe_id'] = chat[chat.length - 1]['swipes'].length; //show that message slot (will be '...' while generating) |
| @@ -8350,7 +8363,7 @@ const swipe_right = () => { | |||
| 8350 | } | 8363 | } |
| 8351 | // handles animated transitions when swipe right, specifically height transitions between messages | 8364 | // handles animated transitions when swipe right, specifically height transitions between messages |
| 8352 | if (run_generate || run_swipe_right) { | 8365 | if (run_generate || run_swipe_right) { |
| 8353 | let this_mes_block = this_mes_div.children('.mes_block').children('.mes_text'); | 8366 | let this_mes_block = this_mes_div.find('.mes_block .mes_text'); |
| 8354 | const this_mes_div_height = this_mes_div[0].scrollHeight; | 8367 | const this_mes_div_height = this_mes_div[0].scrollHeight; |
| 8355 | const this_mes_block_height = this_mes_block[0].scrollHeight; | 8368 | const this_mes_block_height = this_mes_block[0].scrollHeight; |
| 8356 | 8369 | ||
| @@ -9407,9 +9420,9 @@ jQuery(async function () { | |||
| 9407 | 9420 | ||
| 9408 | ///// SWIPE BUTTON CLICKS /////// | 9421 | ///// SWIPE BUTTON CLICKS /////// |
| 9409 | 9422 | ||
| 9410 | $(document).on('click', '.swipe_right', swipe_right); | 9423 | //limit swiping to only last message clicks |
| 9411 | 9424 | $(document).on('click', '.last_mes .swipe_right', swipe_right); | |
| 9412 | $(document).on('click', '.swipe_left', swipe_left); | 9425 | $(document).on('click', '.last_mes .swipe_left', swipe_left); |
| 9413 | 9426 | ||
| 9414 | const debouncedCharacterSearch = debounce((searchQuery) => { | 9427 | const debouncedCharacterSearch = debounce((searchQuery) => { |
| 9415 | entitiesFilter.setFilterData(FILTER_TYPES.SEARCH, searchQuery); | 9428 | entitiesFilter.setFilterData(FILTER_TYPES.SEARCH, searchQuery); |
| @@ -12,6 +12,8 @@ import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from ' | |||
| 12 | import { isFunctionCallingSupported } from '../../openai.js'; | 12 | import { isFunctionCallingSupported } from '../../openai.js'; |
| 13 | import { SlashCommandEnumValue, enumTypes } from '../../slash-commands/SlashCommandEnumValue.js'; | 13 | import { SlashCommandEnumValue, enumTypes } from '../../slash-commands/SlashCommandEnumValue.js'; |
| 14 | import { commonEnumProviders } from '../../slash-commands/SlashCommandCommonEnumsProvider.js'; | 14 | import { commonEnumProviders } from '../../slash-commands/SlashCommandCommonEnumsProvider.js'; |
| 15 | import { slashCommandReturnHelper } from '../../slash-commands/SlashCommandReturnHelper.js'; | ||
| 16 | import { SlashCommandClosure } from '../../slash-commands/SlashCommandClosure.js'; | ||
| 15 | export { MODULE_NAME }; | 17 | export { MODULE_NAME }; |
| 16 | 18 | ||
| 17 | const MODULE_NAME = 'expressions'; | 19 | const MODULE_NAME = 'expressions'; |
| @@ -2134,18 +2136,42 @@ function migrateSettings() { | |||
| 2134 | name: 'classify-expressions', | 2136 | name: 'classify-expressions', |
| 2135 | aliases: ['expressions'], | 2137 | aliases: ['expressions'], |
| 2136 | callback: async (args) => { | 2138 | callback: async (args) => { |
| 2137 | const list = await getExpressionsList(); | 2139 | /** @type {import('../../slash-commands/SlashCommandReturnHelper.js').SlashCommandReturnType} */ |
| 2138 | switch (String(args.format).toLowerCase()) { | 2140 | // @ts-ignore |
| 2139 | case 'json': | 2141 | let returnType = args.return; |
| 2140 | return JSON.stringify(list); | 2142 | |
| 2141 | default: | 2143 | // Old legacy return type handling |
| 2142 | return list.join(', '); | 2144 | if (args.format) { |
| 2145 | toastr.warning(`Legacy argument 'format' with value '${args.format}' is deprecated. Please use 'return' instead. Routing to the correct return type...`, 'Deprecation warning'); | ||
| 2146 | const type = String(args?.format).toLowerCase().trim(); | ||
| 2147 | switch (type) { | ||
| 2148 | case 'json': | ||
| 2149 | returnType = 'object'; | ||
| 2150 | break; | ||
| 2151 | default: | ||
| 2152 | returnType = 'pipe'; | ||
| 2153 | break; | ||
| 2154 | } | ||
| 2143 | } | 2155 | } |
| 2156 | |||
| 2157 | // Now the actual new return type handling | ||
| 2158 | const list = await getExpressionsList(); | ||
| 2159 | |||
| 2160 | return await slashCommandReturnHelper.doReturn(returnType ?? 'pipe', list, { objectToStringFunc: list => list.join(', ') }); | ||
| 2144 | }, | 2161 | }, |
| 2145 | namedArgumentList: [ | 2162 | namedArgumentList: [ |
| 2146 | SlashCommandNamedArgument.fromProps({ | 2163 | SlashCommandNamedArgument.fromProps({ |
| 2164 | name: 'return', | ||
| 2165 | description: 'The way how you want the return value to be provided', | ||
| 2166 | typeList: [ARGUMENT_TYPE.STRING], | ||
| 2167 | defaultValue: 'pipe', | ||
| 2168 | enumList: slashCommandReturnHelper.enumList({ allowObject: true }), | ||
| 2169 | forceEnum: true, | ||
| 2170 | }), | ||
| 2171 | // TODO remove some day | ||
| 2172 | SlashCommandNamedArgument.fromProps({ | ||
| 2147 | name: 'format', | 2173 | name: 'format', |
| 2148 | description: 'The format to return the list in: comma-separated plain text or JSON array. Default is plain text.', | 2174 | description: '!!! DEPRECATED - use "return" instead !!! The format to return the list in: comma-separated plain text or JSON array. Default is plain text.', |
| 2149 | typeList: [ARGUMENT_TYPE.STRING], | 2175 | typeList: [ARGUMENT_TYPE.STRING], |
| 2150 | enumList: [ | 2176 | enumList: [ |
| 2151 | new SlashCommandEnumValue('plain', null, enumTypes.enum, ', '), | 2177 | new SlashCommandEnumValue('plain', null, enumTypes.enum, ', '), |
| @@ -20,7 +20,7 @@ import { | |||
| 20 | } from '../../../script.js'; | 20 | } from '../../../script.js'; |
| 21 | import { getApiUrl, getContext, extension_settings, doExtrasFetch, modules, renderExtensionTemplateAsync, writeExtensionField } from '../../extensions.js'; | 21 | import { getApiUrl, getContext, extension_settings, doExtrasFetch, modules, renderExtensionTemplateAsync, writeExtensionField } from '../../extensions.js'; |
| 22 | import { selected_group } from '../../group-chats.js'; | 22 | import { selected_group } from '../../group-chats.js'; |
| 23 | import { stringFormat, initScrollHeight, resetScrollHeight, getCharaFilename, saveBase64AsFile, getBase64Async, delay, isTrueBoolean, debounce } from '../../utils.js'; | 23 | import { stringFormat, initScrollHeight, resetScrollHeight, getCharaFilename, saveBase64AsFile, getBase64Async, delay, isTrueBoolean, debounce, isFalseBoolean } from '../../utils.js'; |
| 24 | import { getMessageTimeStamp, humanizedDateTime } from '../../RossAscends-mods.js'; | 24 | import { getMessageTimeStamp, humanizedDateTime } from '../../RossAscends-mods.js'; |
| 25 | import { SECRET_KEYS, secret_state, writeSecret } from '../../secrets.js'; | 25 | import { SECRET_KEYS, secret_state, writeSecret } from '../../secrets.js'; |
| 26 | import { getNovelUnlimitedImageGeneration, getNovelAnlas, loadNovelSubscriptionData } from '../../nai-settings.js'; | 26 | import { getNovelUnlimitedImageGeneration, getNovelAnlas, loadNovelSubscriptionData } from '../../nai-settings.js'; |
| @@ -31,6 +31,7 @@ import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from ' | |||
| 31 | import { debounce_timeout } from '../../constants.js'; | 31 | import { debounce_timeout } from '../../constants.js'; |
| 32 | import { SlashCommandEnumValue } from '../../slash-commands/SlashCommandEnumValue.js'; | 32 | import { SlashCommandEnumValue } from '../../slash-commands/SlashCommandEnumValue.js'; |
| 33 | import { POPUP_RESULT, POPUP_TYPE, Popup, callGenericPopup } from '../../popup.js'; | 33 | import { POPUP_RESULT, POPUP_TYPE, Popup, callGenericPopup } from '../../popup.js'; |
| 34 | import { commonEnumProviders } from '../../slash-commands/SlashCommandCommonEnumsProvider.js'; | ||
| 34 | export { MODULE_NAME }; | 35 | export { MODULE_NAME }; |
| 35 | 36 | ||
| 36 | const MODULE_NAME = 'sd'; | 37 | const MODULE_NAME = 'sd'; |
| @@ -221,7 +222,6 @@ const defaultSettings = { | |||
| 221 | 222 | ||
| 222 | // Refine mode | 223 | // Refine mode |
| 223 | refine_mode: false, | 224 | refine_mode: false, |
| 224 | expand: false, | ||
| 225 | interactive_mode: false, | 225 | interactive_mode: false, |
| 226 | multimodal_captioning: false, | 226 | multimodal_captioning: false, |
| 227 | snap: false, | 227 | snap: false, |
| @@ -240,7 +240,7 @@ const defaultSettings = { | |||
| 240 | drawthings_auth: '', | 240 | drawthings_auth: '', |
| 241 | 241 | ||
| 242 | hr_upscaler: 'Latent', | 242 | hr_upscaler: 'Latent', |
| 243 | hr_scale: 2.0, | 243 | hr_scale: 1.0, |
| 244 | hr_scale_min: 1.0, | 244 | hr_scale_min: 1.0, |
| 245 | hr_scale_max: 4.0, | 245 | hr_scale_max: 4.0, |
| 246 | hr_scale_step: 0.1, | 246 | hr_scale_step: 0.1, |
| @@ -260,10 +260,6 @@ const defaultSettings = { | |||
| 260 | clip_skip: 1, | 260 | clip_skip: 1, |
| 261 | 261 | ||
| 262 | // NovelAI settings | 262 | // NovelAI settings |
| 263 | novel_upscale_ratio_min: 1.0, | ||
| 264 | novel_upscale_ratio_max: 4.0, | ||
| 265 | novel_upscale_ratio_step: 0.1, | ||
| 266 | novel_upscale_ratio: 1.0, | ||
| 267 | novel_anlas_guard: false, | 263 | novel_anlas_guard: false, |
| 268 | novel_sm: false, | 264 | novel_sm: false, |
| 269 | novel_sm_dyn: false, | 265 | novel_sm_dyn: false, |
| @@ -416,7 +412,6 @@ async function loadSettings() { | |||
| 416 | $('#sd_hr_scale').val(extension_settings.sd.hr_scale).trigger('input'); | 412 | $('#sd_hr_scale').val(extension_settings.sd.hr_scale).trigger('input'); |
| 417 | $('#sd_denoising_strength').val(extension_settings.sd.denoising_strength).trigger('input'); | 413 | $('#sd_denoising_strength').val(extension_settings.sd.denoising_strength).trigger('input'); |
| 418 | $('#sd_hr_second_pass_steps').val(extension_settings.sd.hr_second_pass_steps).trigger('input'); | 414 | $('#sd_hr_second_pass_steps').val(extension_settings.sd.hr_second_pass_steps).trigger('input'); |
| 419 | $('#sd_novel_upscale_ratio').val(extension_settings.sd.novel_upscale_ratio).trigger('input'); | ||
| 420 | $('#sd_novel_anlas_guard').prop('checked', extension_settings.sd.novel_anlas_guard); | 415 | $('#sd_novel_anlas_guard').prop('checked', extension_settings.sd.novel_anlas_guard); |
| 421 | $('#sd_novel_sm').prop('checked', extension_settings.sd.novel_sm); | 416 | $('#sd_novel_sm').prop('checked', extension_settings.sd.novel_sm); |
| 422 | $('#sd_novel_sm_dyn').prop('checked', extension_settings.sd.novel_sm_dyn); | 417 | $('#sd_novel_sm_dyn').prop('checked', extension_settings.sd.novel_sm_dyn); |
| @@ -430,7 +425,6 @@ async function loadSettings() { | |||
| 430 | $('#sd_restore_faces').prop('checked', extension_settings.sd.restore_faces); | 425 | $('#sd_restore_faces').prop('checked', extension_settings.sd.restore_faces); |
| 431 | $('#sd_enable_hr').prop('checked', extension_settings.sd.enable_hr); | 426 | $('#sd_enable_hr').prop('checked', extension_settings.sd.enable_hr); |
| 432 | $('#sd_refine_mode').prop('checked', extension_settings.sd.refine_mode); | 427 | $('#sd_refine_mode').prop('checked', extension_settings.sd.refine_mode); |
| 433 | $('#sd_expand').prop('checked', extension_settings.sd.expand); | ||
| 434 | $('#sd_multimodal_captioning').prop('checked', extension_settings.sd.multimodal_captioning); | 428 | $('#sd_multimodal_captioning').prop('checked', extension_settings.sd.multimodal_captioning); |
| 435 | $('#sd_auto_url').val(extension_settings.sd.auto_url); | 429 | $('#sd_auto_url').val(extension_settings.sd.auto_url); |
| 436 | $('#sd_auto_auth').val(extension_settings.sd.auto_auth); | 430 | $('#sd_auto_auth').val(extension_settings.sd.auto_auth); |
| @@ -644,37 +638,13 @@ async function onSaveStyleClick() { | |||
| 644 | saveSettingsDebounced(); | 638 | saveSettingsDebounced(); |
| 645 | } | 639 | } |
| 646 | 640 | ||
| 647 | async function expandPrompt(prompt) { | ||
| 648 | try { | ||
| 649 | const response = await fetch('/api/sd/expand', { | ||
| 650 | method: 'POST', | ||
| 651 | headers: getRequestHeaders(), | ||
| 652 | body: JSON.stringify({ prompt: prompt }), | ||
| 653 | }); | ||
| 654 | |||
| 655 | if (!response.ok) { | ||
| 656 | throw new Error('API returned an error.'); | ||
| 657 | } | ||
| 658 | |||
| 659 | const data = await response.json(); | ||
| 660 | return data.prompt; | ||
| 661 | } catch { | ||
| 662 | return prompt; | ||
| 663 | } | ||
| 664 | } | ||
| 665 | |||
| 666 | /** | 641 | /** |
| 667 | * Modifies prompt based on auto-expansion and user inputs. | 642 | * Modifies prompt based on user inputs. |
| 668 | * @param {string} prompt Prompt to refine | 643 | * @param {string} prompt Prompt to refine |
| 669 | * @param {boolean} allowExpand Whether to allow auto-expansion | ||
| 670 | * @param {boolean} isNegative Whether the prompt is a negative one | 644 | * @param {boolean} isNegative Whether the prompt is a negative one |
| 671 | * @returns {Promise<string>} Refined prompt | 645 | * @returns {Promise<string>} Refined prompt |
| 672 | */ | 646 | */ |
| 673 | async function refinePrompt(prompt, allowExpand, isNegative = false) { | 647 | async function refinePrompt(prompt, isNegative) { |
| 674 | if (allowExpand && extension_settings.sd.expand) { | ||
| 675 | prompt = await expandPrompt(prompt); | ||
| 676 | } | ||
| 677 | |||
| 678 | if (extension_settings.sd.refine_mode) { | 648 | if (extension_settings.sd.refine_mode) { |
| 679 | const text = isNegative ? '<h3>Review and edit the <i>negative</i> prompt:</h3>' : '<h3>Review and edit the prompt:</h3>'; | 649 | const text = isNegative ? '<h3>Review and edit the <i>negative</i> prompt:</h3>' : '<h3>Review and edit the prompt:</h3>'; |
| 680 | const refinedPrompt = await callGenericPopup(text + 'Press "Cancel" to abort the image generation.', POPUP_TYPE.INPUT, prompt.trim(), { rows: 5, okButton: 'Continue' }); | 650 | const refinedPrompt = await callGenericPopup(text + 'Press "Cancel" to abort the image generation.', POPUP_TYPE.INPUT, prompt.trim(), { rows: 5, okButton: 'Continue' }); |
| @@ -800,11 +770,6 @@ function combinePrefixes(str1, str2, macro = '') { | |||
| 800 | return process(result); | 770 | return process(result); |
| 801 | } | 771 | } |
| 802 | 772 | ||
| 803 | function onExpandInput() { | ||
| 804 | extension_settings.sd.expand = !!$(this).prop('checked'); | ||
| 805 | saveSettingsDebounced(); | ||
| 806 | } | ||
| 807 | |||
| 808 | function onRefineModeInput() { | 773 | function onRefineModeInput() { |
| 809 | extension_settings.sd.refine_mode = !!$('#sd_refine_mode').prop('checked'); | 774 | extension_settings.sd.refine_mode = !!$('#sd_refine_mode').prop('checked'); |
| 810 | saveSettingsDebounced(); | 775 | saveSettingsDebounced(); |
| @@ -969,12 +934,6 @@ async function onViewAnlasClick() { | |||
| 969 | toastr.info(`Free image generation: ${unlimitedGeneration ? 'Yes' : 'No'}`, `Anlas: ${anlas}`); | 934 | toastr.info(`Free image generation: ${unlimitedGeneration ? 'Yes' : 'No'}`, `Anlas: ${anlas}`); |
| 970 | } | 935 | } |
| 971 | 936 | ||
| 972 | function onNovelUpscaleRatioInput() { | ||
| 973 | extension_settings.sd.novel_upscale_ratio = Number($('#sd_novel_upscale_ratio').val()); | ||
| 974 | $('#sd_novel_upscale_ratio_value').val(extension_settings.sd.novel_upscale_ratio.toFixed(1)); | ||
| 975 | saveSettingsDebounced(); | ||
| 976 | } | ||
| 977 | |||
| 978 | function onNovelAnlasGuardInput() { | 937 | function onNovelAnlasGuardInput() { |
| 979 | extension_settings.sd.novel_anlas_guard = !!$('#sd_novel_anlas_guard').prop('checked'); | 938 | extension_settings.sd.novel_anlas_guard = !!$('#sd_novel_anlas_guard').prop('checked'); |
| 980 | saveSettingsDebounced(); | 939 | saveSettingsDebounced(); |
| @@ -2273,6 +2232,25 @@ function getRawLastMessage() { | |||
| 2273 | } | 2232 | } |
| 2274 | 2233 | ||
| 2275 | /** | 2234 | /** |
| 2235 | * Ensure that the selected option exists in the dropdown. | ||
| 2236 | * @param {string} setting Setting key | ||
| 2237 | * @param {string} selector Dropdown selector | ||
| 2238 | * @returns {void} | ||
| 2239 | */ | ||
| 2240 | function ensureSelectionExists(setting, selector) { | ||
| 2241 | /** @type {HTMLSelectElement} */ | ||
| 2242 | const selectElement = document.querySelector(selector); | ||
| 2243 | if (!selectElement) { | ||
| 2244 | return; | ||
| 2245 | } | ||
| 2246 | const options = Array.from(selectElement.options); | ||
| 2247 | const value = extension_settings.sd[setting]; | ||
| 2248 | if (selectElement.selectedOptions.length && !options.some(option => option.value === value)) { | ||
| 2249 | extension_settings.sd[setting] = selectElement.selectedOptions[0].value; | ||
| 2250 | } | ||
| 2251 | } | ||
| 2252 | |||
| 2253 | /** | ||
| 2276 | * Generates an image based on the given trigger word. | 2254 | * Generates an image based on the given trigger word. |
| 2277 | * @param {string} initiator The initiator of the image generation | 2255 | * @param {string} initiator The initiator of the image generation |
| 2278 | * @param {Record<string, object>} args Command arguments | 2256 | * @param {Record<string, object>} args Command arguments |
| @@ -2292,8 +2270,8 @@ async function generatePicture(initiator, args, trigger, message, callback) { | |||
| 2292 | return; | 2270 | return; |
| 2293 | } | 2271 | } |
| 2294 | 2272 | ||
| 2295 | extension_settings.sd.sampler = $('#sd_sampler').find(':selected').val(); | 2273 | ensureSelectionExists('sampler', '#sd_sampler'); |
| 2296 | extension_settings.sd.model = $('#sd_model').find(':selected').val(); | 2274 | ensureSelectionExists('model', '#sd_model'); |
| 2297 | 2275 | ||
| 2298 | trigger = trigger.trim(); | 2276 | trigger = trigger.trim(); |
| 2299 | const generationType = getGenerationType(trigger); | 2277 | const generationType = getGenerationType(trigger); |
| @@ -2441,7 +2419,7 @@ async function getPrompt(generationType, message, trigger, quietPrompt, combineN | |||
| 2441 | } | 2419 | } |
| 2442 | 2420 | ||
| 2443 | if (generationType !== generationMode.FREE) { | 2421 | if (generationType !== generationMode.FREE) { |
| 2444 | prompt = await refinePrompt(prompt, true); | 2422 | prompt = await refinePrompt(prompt, false); |
| 2445 | } | 2423 | } |
| 2446 | 2424 | ||
| 2447 | return prompt; | 2425 | return prompt; |
| @@ -2469,7 +2447,7 @@ function generateFreeModePrompt(trigger, combineNegatives) { | |||
| 2469 | return message.original_avatar.replace(/\.[^/.]+$/, ''); | 2447 | return message.original_avatar.replace(/\.[^/.]+$/, ''); |
| 2470 | } | 2448 | } |
| 2471 | } | 2449 | } |
| 2472 | throw new Error('No usable messages found.'); | 2450 | return ''; |
| 2473 | }; | 2451 | }; |
| 2474 | 2452 | ||
| 2475 | const key = getLastCharacterKey(); | 2453 | const key = getLastCharacterKey(); |
| @@ -3031,7 +3009,7 @@ async function generateNovelImage(prompt, negativePrompt, signal) { | |||
| 3031 | width: width, | 3009 | width: width, |
| 3032 | height: height, | 3010 | height: height, |
| 3033 | negative_prompt: negativePrompt, | 3011 | negative_prompt: negativePrompt, |
| 3034 | upscale_ratio: extension_settings.sd.novel_upscale_ratio, | 3012 | upscale_ratio: extension_settings.sd.hr_scale, |
| 3035 | decrisper: extension_settings.sd.novel_decrisper, | 3013 | decrisper: extension_settings.sd.novel_decrisper, |
| 3036 | sm: sm, | 3014 | sm: sm, |
| 3037 | sm_dyn: sm_dyn, | 3015 | sm_dyn: sm_dyn, |
| @@ -3613,8 +3591,8 @@ async function sdMessageButton(e) { | |||
| 3613 | try { | 3591 | try { |
| 3614 | setBusyIcon(true); | 3592 | setBusyIcon(true); |
| 3615 | if (hasSavedImage) { | 3593 | if (hasSavedImage) { |
| 3616 | const prompt = await refinePrompt(message.extra.title, false, false); | 3594 | const prompt = await refinePrompt(message.extra.title, false); |
| 3617 | const negative = hasSavedNegative ? await refinePrompt(message.extra.negative, false, true) : ''; | 3595 | const negative = hasSavedNegative ? await refinePrompt(message.extra.negative, true) : ''; |
| 3618 | message.extra.title = prompt; | 3596 | message.extra.title = prompt; |
| 3619 | 3597 | ||
| 3620 | const generationType = message?.extra?.generationType ?? generationMode.FREE; | 3598 | const generationType = message?.extra?.generationType ?? generationMode.FREE; |
| @@ -3756,8 +3734,8 @@ async function onImageSwiped({ message, element, direction }) { | |||
| 3756 | eventSource.once(CUSTOM_STOP_EVENT, stopListener); | 3734 | eventSource.once(CUSTOM_STOP_EVENT, stopListener); |
| 3757 | const callback = () => { }; | 3735 | const callback = () => { }; |
| 3758 | const hasNegative = message.extra.negative; | 3736 | const hasNegative = message.extra.negative; |
| 3759 | const prompt = await refinePrompt(message.extra.title, false, false); | 3737 | const prompt = await refinePrompt(message.extra.title, false); |
| 3760 | const negativePromptPrefix = hasNegative ? await refinePrompt(message.extra.negative, false, true) : ''; | 3738 | const negativePromptPrefix = hasNegative ? await refinePrompt(message.extra.negative, true) : ''; |
| 3761 | const characterName = context.groupId | 3739 | const characterName = context.groupId |
| 3762 | ? context.groups[Object.keys(context.groups).filter(x => context.groups[x].id === context.groupId)[0]]?.id?.toString() | 3740 | ? context.groups[Object.keys(context.groups).filter(x => context.groups[x].id === context.groupId)[0]]?.id?.toString() |
| 3763 | : context.characters[context.characterId]?.name; | 3741 | : context.characters[context.characterId]?.name; |
| @@ -3788,12 +3766,85 @@ async function onImageSwiped({ message, element, direction }) { | |||
| 3788 | await context.saveChat(); | 3766 | await context.saveChat(); |
| 3789 | } | 3767 | } |
| 3790 | 3768 | ||
| 3769 | /** | ||
| 3770 | * Applies the command arguments to the extension settings. | ||
| 3771 | * @typedef {import('../../slash-commands/SlashCommand.js').NamedArguments} NamedArguments | ||
| 3772 | * @typedef {import('../../slash-commands/SlashCommand.js').NamedArgumentsCapture} NamedArgumentsCapture | ||
| 3773 | * @param {NamedArguments | NamedArgumentsCapture} args - Command arguments | ||
| 3774 | * @returns {Record<string, any>} - Current settings before applying the command arguments | ||
| 3775 | */ | ||
| 3776 | function applyCommandArguments(args) { | ||
| 3777 | const overrideSettings = {}; | ||
| 3778 | const currentSettings = {}; | ||
| 3779 | const settingMap = { | ||
| 3780 | 'edit': 'refine_mode', | ||
| 3781 | 'extend': 'free_extend', | ||
| 3782 | 'multimodal': 'multimodal_captioning', | ||
| 3783 | 'seed': 'seed', | ||
| 3784 | 'width': 'width', | ||
| 3785 | 'height': 'height', | ||
| 3786 | 'steps': 'steps', | ||
| 3787 | 'cfg': 'scale', | ||
| 3788 | 'skip': 'clip_skip', | ||
| 3789 | 'model': 'model', | ||
| 3790 | 'sampler': 'sampler', | ||
| 3791 | 'scheduler': 'scheduler', | ||
| 3792 | 'vae': 'vae', | ||
| 3793 | 'upscaler': 'hr_upscaler', | ||
| 3794 | 'scale': 'hr_scale', | ||
| 3795 | 'hires': 'enable_hr', | ||
| 3796 | 'denoise': 'denoising_strength', | ||
| 3797 | '2ndpass': 'hr_second_pass_steps', | ||
| 3798 | 'faces': 'restore_faces', | ||
| 3799 | }; | ||
| 3800 | |||
| 3801 | for (const [param, setting] of Object.entries(settingMap)) { | ||
| 3802 | if (args[param] === undefined || defaultSettings[setting] === undefined) { | ||
| 3803 | continue; | ||
| 3804 | } | ||
| 3805 | currentSettings[setting] = extension_settings.sd[setting]; | ||
| 3806 | const value = String(args[param]); | ||
| 3807 | const type = typeof defaultSettings[setting]; | ||
| 3808 | switch (type) { | ||
| 3809 | case 'boolean': | ||
| 3810 | overrideSettings[setting] = isTrueBoolean(value) || !isFalseBoolean(value); | ||
| 3811 | break; | ||
| 3812 | case 'number': | ||
| 3813 | overrideSettings[setting] = Number(value); | ||
| 3814 | break; | ||
| 3815 | default: | ||
| 3816 | overrideSettings[setting] = value; | ||
| 3817 | break; | ||
| 3818 | } | ||
| 3819 | } | ||
| 3820 | |||
| 3821 | Object.assign(extension_settings.sd, overrideSettings); | ||
| 3822 | return currentSettings; | ||
| 3823 | } | ||
| 3824 | |||
| 3791 | jQuery(async () => { | 3825 | jQuery(async () => { |
| 3792 | await addSDGenButtons(); | 3826 | await addSDGenButtons(); |
| 3793 | 3827 | ||
| 3828 | const getSelectEnumProvider = (id, text) => () => Array.from(document.querySelectorAll(`#${id} > [value]`)).map(x => new SlashCommandEnumValue(x.getAttribute('value'), text ? x.textContent : null)); | ||
| 3829 | |||
| 3794 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | 3830 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 3795 | name: 'imagine', | 3831 | name: 'imagine', |
| 3796 | callback: (args, trigger) => generatePicture(initiators.command, args, String(trigger)), | 3832 | returns: 'URL of the generated image, or an empty string if the generation failed', |
| 3833 | callback: async (args, trigger) => { | ||
| 3834 | const currentSettings = applyCommandArguments(args); | ||
| 3835 | |||
| 3836 | try { | ||
| 3837 | return await generatePicture(initiators.command, args, String(trigger)); | ||
| 3838 | } catch (error) { | ||
| 3839 | console.error('Failed to generate image:', error); | ||
| 3840 | return ''; | ||
| 3841 | } finally { | ||
| 3842 | if (Object.keys(currentSettings).length) { | ||
| 3843 | Object.assign(extension_settings.sd, currentSettings); | ||
| 3844 | saveSettingsDebounced(); | ||
| 3845 | } | ||
| 3846 | } | ||
| 3847 | }, | ||
| 3797 | aliases: ['sd', 'img', 'image'], | 3848 | aliases: ['sd', 'img', 'image'], |
| 3798 | namedArgumentList: [ | 3849 | namedArgumentList: [ |
| 3799 | new SlashCommandNamedArgument( | 3850 | new SlashCommandNamedArgument( |
| @@ -3803,6 +3854,164 @@ jQuery(async () => { | |||
| 3803 | name: 'negative', | 3854 | name: 'negative', |
| 3804 | description: 'negative prompt prefix', | 3855 | description: 'negative prompt prefix', |
| 3805 | typeList: [ARGUMENT_TYPE.STRING], | 3856 | typeList: [ARGUMENT_TYPE.STRING], |
| 3857 | isRequired: false, | ||
| 3858 | acceptsMultiple: false, | ||
| 3859 | }), | ||
| 3860 | SlashCommandNamedArgument.fromProps({ | ||
| 3861 | name: 'extend', | ||
| 3862 | description: 'auto-extend free mode prompts with the LLM', | ||
| 3863 | typeList: [ARGUMENT_TYPE.BOOLEAN], | ||
| 3864 | enumProvider: commonEnumProviders.boolean('trueFalse'), | ||
| 3865 | isRequired: false, | ||
| 3866 | acceptsMultiple: false, | ||
| 3867 | }), | ||
| 3868 | SlashCommandNamedArgument.fromProps({ | ||
| 3869 | name: 'edit', | ||
| 3870 | description: 'edit the prompt before generation', | ||
| 3871 | typeList: [ARGUMENT_TYPE.BOOLEAN], | ||
| 3872 | enumProvider: commonEnumProviders.boolean('trueFalse'), | ||
| 3873 | isRequired: false, | ||
| 3874 | acceptsMultiple: false, | ||
| 3875 | }), | ||
| 3876 | SlashCommandNamedArgument.fromProps({ | ||
| 3877 | name: 'multimodal', | ||
| 3878 | description: 'use multimodal captioning (for portraits only)', | ||
| 3879 | typeList: [ARGUMENT_TYPE.BOOLEAN], | ||
| 3880 | enumProvider: commonEnumProviders.boolean('trueFalse'), | ||
| 3881 | isRequired: false, | ||
| 3882 | acceptsMultiple: false, | ||
| 3883 | }), | ||
| 3884 | SlashCommandNamedArgument.fromProps({ | ||
| 3885 | name: 'snap', | ||
| 3886 | description: 'snap auto-adjusted dimensions to the nearest known resolution (portraits and backgrounds only)', | ||
| 3887 | typeList: [ARGUMENT_TYPE.BOOLEAN], | ||
| 3888 | enumProvider: commonEnumProviders.boolean('trueFalse'), | ||
| 3889 | isRequired: false, | ||
| 3890 | acceptsMultiple: false, | ||
| 3891 | }), | ||
| 3892 | SlashCommandNamedArgument.fromProps({ | ||
| 3893 | name: 'seed', | ||
| 3894 | description: 'random seed', | ||
| 3895 | isRequired: false, | ||
| 3896 | typeList: [ARGUMENT_TYPE.NUMBER], | ||
| 3897 | acceptsMultiple: false, | ||
| 3898 | }), | ||
| 3899 | SlashCommandNamedArgument.fromProps({ | ||
| 3900 | name: 'width', | ||
| 3901 | description: 'image width', | ||
| 3902 | isRequired: false, | ||
| 3903 | typeList: [ARGUMENT_TYPE.NUMBER], | ||
| 3904 | acceptsMultiple: false, | ||
| 3905 | }), | ||
| 3906 | SlashCommandNamedArgument.fromProps({ | ||
| 3907 | name: 'height', | ||
| 3908 | description: 'image height', | ||
| 3909 | isRequired: false, | ||
| 3910 | typeList: [ARGUMENT_TYPE.NUMBER], | ||
| 3911 | acceptsMultiple: false, | ||
| 3912 | }), | ||
| 3913 | SlashCommandNamedArgument.fromProps({ | ||
| 3914 | name: 'steps', | ||
| 3915 | description: 'number of steps', | ||
| 3916 | isRequired: false, | ||
| 3917 | typeList: [ARGUMENT_TYPE.NUMBER], | ||
| 3918 | acceptsMultiple: false, | ||
| 3919 | }), | ||
| 3920 | SlashCommandNamedArgument.fromProps({ | ||
| 3921 | name: 'cfg', | ||
| 3922 | description: 'CFG scale', | ||
| 3923 | isRequired: false, | ||
| 3924 | typeList: [ARGUMENT_TYPE.NUMBER], | ||
| 3925 | acceptsMultiple: false, | ||
| 3926 | }), | ||
| 3927 | SlashCommandNamedArgument.fromProps({ | ||
| 3928 | name: 'skip', | ||
| 3929 | description: 'CLIP skip layers', | ||
| 3930 | isRequired: false, | ||
| 3931 | typeList: [ARGUMENT_TYPE.NUMBER], | ||
| 3932 | acceptsMultiple: false, | ||
| 3933 | }), | ||
| 3934 | SlashCommandNamedArgument.fromProps({ | ||
| 3935 | name: 'model', | ||
| 3936 | description: 'model override', | ||
| 3937 | isRequired: false, | ||
| 3938 | typeList: [ARGUMENT_TYPE.STRING], | ||
| 3939 | acceptsMultiple: false, | ||
| 3940 | forceEnum: true, | ||
| 3941 | enumProvider: getSelectEnumProvider('sd_model', true), | ||
| 3942 | }), | ||
| 3943 | SlashCommandNamedArgument.fromProps({ | ||
| 3944 | name: 'sampler', | ||
| 3945 | description: 'sampler override', | ||
| 3946 | isRequired: false, | ||
| 3947 | typeList: [ARGUMENT_TYPE.STRING], | ||
| 3948 | acceptsMultiple: false, | ||
| 3949 | forceEnum: true, | ||
| 3950 | enumProvider: getSelectEnumProvider('sd_sampler', false), | ||
| 3951 | }), | ||
| 3952 | SlashCommandNamedArgument.fromProps({ | ||
| 3953 | name: 'scheduler', | ||
| 3954 | description: 'scheduler override', | ||
| 3955 | isRequired: false, | ||
| 3956 | typeList: [ARGUMENT_TYPE.STRING], | ||
| 3957 | acceptsMultiple: false, | ||
| 3958 | forceEnum: true, | ||
| 3959 | enumProvider: getSelectEnumProvider('sd_scheduler', false), | ||
| 3960 | }), | ||
| 3961 | SlashCommandNamedArgument.fromProps({ | ||
| 3962 | name: 'vae', | ||
| 3963 | description: 'VAE name override', | ||
| 3964 | isRequired: false, | ||
| 3965 | typeList: [ARGUMENT_TYPE.STRING], | ||
| 3966 | acceptsMultiple: false, | ||
| 3967 | forceEnum: true, | ||
| 3968 | enumProvider: getSelectEnumProvider('sd_vae', false), | ||
| 3969 | }), | ||
| 3970 | SlashCommandNamedArgument.fromProps({ | ||
| 3971 | name: 'upscaler', | ||
| 3972 | description: 'upscaler override', | ||
| 3973 | isRequired: false, | ||
| 3974 | typeList: [ARGUMENT_TYPE.STRING], | ||
| 3975 | acceptsMultiple: false, | ||
| 3976 | forceEnum: true, | ||
| 3977 | enumProvider: getSelectEnumProvider('sd_hr_upscaler', false), | ||
| 3978 | }), | ||
| 3979 | SlashCommandNamedArgument.fromProps({ | ||
| 3980 | name: 'hires', | ||
| 3981 | description: 'enable high-res fix', | ||
| 3982 | isRequired: false, | ||
| 3983 | typeList: [ARGUMENT_TYPE.BOOLEAN], | ||
| 3984 | acceptsMultiple: false, | ||
| 3985 | enumProvider: commonEnumProviders.boolean('trueFalse'), | ||
| 3986 | }), | ||
| 3987 | SlashCommandNamedArgument.fromProps({ | ||
| 3988 | name: 'scale', | ||
| 3989 | description: 'upscale amount', | ||
| 3990 | isRequired: false, | ||
| 3991 | typeList: [ARGUMENT_TYPE.NUMBER], | ||
| 3992 | acceptsMultiple: false, | ||
| 3993 | }), | ||
| 3994 | SlashCommandNamedArgument.fromProps({ | ||
| 3995 | name: 'denoise', | ||
| 3996 | description: 'denoising strength', | ||
| 3997 | isRequired: false, | ||
| 3998 | typeList: [ARGUMENT_TYPE.NUMBER], | ||
| 3999 | acceptsMultiple: false, | ||
| 4000 | }), | ||
| 4001 | SlashCommandNamedArgument.fromProps({ | ||
| 4002 | name: '2ndpass', | ||
| 4003 | description: 'second pass steps', | ||
| 4004 | isRequired: false, | ||
| 4005 | typeList: [ARGUMENT_TYPE.NUMBER], | ||
| 4006 | acceptsMultiple: false, | ||
| 4007 | }), | ||
| 4008 | SlashCommandNamedArgument.fromProps({ | ||
| 4009 | name: 'faces', | ||
| 4010 | description: 'restore faces', | ||
| 4011 | isRequired: false, | ||
| 4012 | typeList: [ARGUMENT_TYPE.BOOLEAN], | ||
| 4013 | acceptsMultiple: false, | ||
| 4014 | enumProvider: commonEnumProviders.boolean('trueFalse'), | ||
| 3806 | }), | 4015 | }), |
| 3807 | ], | 4016 | ], |
| 3808 | unnamedArgumentList: [ | 4017 | unnamedArgumentList: [ |
| @@ -3824,6 +4033,66 @@ jQuery(async () => { | |||
| 3824 | })); | 4033 | })); |
| 3825 | 4034 | ||
| 3826 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | 4035 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 4036 | name: 'imagine-source', | ||
| 4037 | aliases: ['sd-source', 'img-source'], | ||
| 4038 | returns: 'a name of the current generation source', | ||
| 4039 | unnamedArgumentList: [ | ||
| 4040 | SlashCommandArgument.fromProps({ | ||
| 4041 | description: 'source name', | ||
| 4042 | typeList: [ARGUMENT_TYPE.STRING], | ||
| 4043 | isRequired: false, | ||
| 4044 | forceEnum: true, | ||
| 4045 | enumProvider: getSelectEnumProvider('sd_source', true), | ||
| 4046 | }), | ||
| 4047 | ], | ||
| 4048 | helpString: 'If an argument is provided, change the source of the image generation, e.g. <code>/imagine-source comfy</code>. Returns the current source.', | ||
| 4049 | callback: async (_args, name) => { | ||
| 4050 | if (!name) { | ||
| 4051 | return extension_settings.sd.source; | ||
| 4052 | } | ||
| 4053 | const isKnownSource = Object.keys(sources).includes(String(name)); | ||
| 4054 | if (!isKnownSource) { | ||
| 4055 | throw new Error('The value provided is not a valid image generation source.'); | ||
| 4056 | } | ||
| 4057 | const option = document.querySelector(`#sd_source [value="${name}"]`); | ||
| 4058 | if (!(option instanceof HTMLOptionElement)) { | ||
| 4059 | throw new Error('Could not find the source option in the dropdown.'); | ||
| 4060 | } | ||
| 4061 | option.selected = true; | ||
| 4062 | await onSourceChange(); | ||
| 4063 | return extension_settings.sd.source; | ||
| 4064 | }, | ||
| 4065 | })); | ||
| 4066 | |||
| 4067 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 4068 | name: 'imagine-style', | ||
| 4069 | aliases: ['sd-style', 'img-style'], | ||
| 4070 | returns: 'a name of the current style', | ||
| 4071 | unnamedArgumentList: [ | ||
| 4072 | SlashCommandArgument.fromProps({ | ||
| 4073 | description: 'style name', | ||
| 4074 | typeList: [ARGUMENT_TYPE.STRING], | ||
| 4075 | isRequired: false, | ||
| 4076 | forceEnum: true, | ||
| 4077 | enumProvider: getSelectEnumProvider('sd_style', false), | ||
| 4078 | }), | ||
| 4079 | ], | ||
| 4080 | helpString: 'If an argument is provided, change the style of the image generation, e.g. <code>/imagine-style MyStyle</code>. Returns the current style.', | ||
| 4081 | callback: async (_args, name) => { | ||
| 4082 | if (!name) { | ||
| 4083 | return extension_settings.sd.style; | ||
| 4084 | } | ||
| 4085 | const option = document.querySelector(`#sd_style [value="${name}"]`); | ||
| 4086 | if (!(option instanceof HTMLOptionElement)) { | ||
| 4087 | throw new Error('Could not find the style option in the dropdown.'); | ||
| 4088 | } | ||
| 4089 | option.selected = true; | ||
| 4090 | onStyleSelect(); | ||
| 4091 | return extension_settings.sd.style; | ||
| 4092 | }, | ||
| 4093 | })); | ||
| 4094 | |||
| 4095 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 3827 | name: 'imagine-comfy-workflow', | 4096 | name: 'imagine-comfy-workflow', |
| 3828 | callback: changeComfyWorkflow, | 4097 | callback: changeComfyWorkflow, |
| 3829 | aliases: ['icw'], | 4098 | aliases: ['icw'], |
| @@ -3832,7 +4101,7 @@ jQuery(async () => { | |||
| 3832 | description: 'workflow name', | 4101 | description: 'workflow name', |
| 3833 | typeList: [ARGUMENT_TYPE.STRING], | 4102 | typeList: [ARGUMENT_TYPE.STRING], |
| 3834 | isRequired: true, | 4103 | isRequired: true, |
| 3835 | enumProvider: () => Array.from(document.querySelectorAll('#sd_comfy_workflow > [value]')).map(x => x.getAttribute('value')).map(workflow => new SlashCommandEnumValue(workflow)), | 4104 | enumProvider: getSelectEnumProvider('sd_comfy_workflow', false), |
| 3836 | }), | 4105 | }), |
| 3837 | ], | 4106 | ], |
| 3838 | helpString: '(workflowName) - change the workflow to be used for image generation with ComfyUI, e.g. <pre><code>/imagine-comfy-workflow MyWorkflow</code></pre>', | 4107 | helpString: '(workflowName) - change the workflow to be used for image generation with ComfyUI, e.g. <pre><code>/imagine-comfy-workflow MyWorkflow</code></pre>', |
| @@ -3874,7 +4143,6 @@ jQuery(async () => { | |||
| 3874 | $('#sd_hr_scale').on('input', onHrScaleInput); | 4143 | $('#sd_hr_scale').on('input', onHrScaleInput); |
| 3875 | $('#sd_denoising_strength').on('input', onDenoisingStrengthInput); | 4144 | $('#sd_denoising_strength').on('input', onDenoisingStrengthInput); |
| 3876 | $('#sd_hr_second_pass_steps').on('input', onHrSecondPassStepsInput); | 4145 | $('#sd_hr_second_pass_steps').on('input', onHrSecondPassStepsInput); |
| 3877 | $('#sd_novel_upscale_ratio').on('input', onNovelUpscaleRatioInput); | ||
| 3878 | $('#sd_novel_anlas_guard').on('input', onNovelAnlasGuardInput); | 4146 | $('#sd_novel_anlas_guard').on('input', onNovelAnlasGuardInput); |
| 3879 | $('#sd_novel_view_anlas').on('click', onViewAnlasClick); | 4147 | $('#sd_novel_view_anlas').on('click', onViewAnlasClick); |
| 3880 | $('#sd_novel_sm').on('input', onNovelSmInput); | 4148 | $('#sd_novel_sm').on('input', onNovelSmInput); |
| @@ -3887,7 +4155,6 @@ jQuery(async () => { | |||
| 3887 | $('#sd_comfy_open_workflow_editor').on('click', onComfyOpenWorkflowEditorClick); | 4155 | $('#sd_comfy_open_workflow_editor').on('click', onComfyOpenWorkflowEditorClick); |
| 3888 | $('#sd_comfy_new_workflow').on('click', onComfyNewWorkflowClick); | 4156 | $('#sd_comfy_new_workflow').on('click', onComfyNewWorkflowClick); |
| 3889 | $('#sd_comfy_delete_workflow').on('click', onComfyDeleteWorkflowClick); | 4157 | $('#sd_comfy_delete_workflow').on('click', onComfyDeleteWorkflowClick); |
| 3890 | $('#sd_expand').on('input', onExpandInput); | ||
| 3891 | $('#sd_style').on('change', onStyleSelect); | 4158 | $('#sd_style').on('change', onStyleSelect); |
| 3892 | $('#sd_save_style').on('click', onSaveStyleClick); | 4159 | $('#sd_save_style').on('click', onSaveStyleClick); |
| 3893 | $('#sd_delete_style').on('click', onDeleteStyleClick); | 4160 | $('#sd_delete_style').on('click', onDeleteStyleClick); |
| @@ -27,11 +27,6 @@ | |||
| 27 | <span data-i18n="sd_free_extend_txt">Extend free mode prompts</span> | 27 | <span data-i18n="sd_free_extend_txt">Extend free mode prompts</span> |
| 28 | <small data-i18n="sd_free_extend_small">(interactive/commands)</small> | 28 | <small data-i18n="sd_free_extend_small">(interactive/commands)</small> |
| 29 | </label> | 29 | </label> |
| 30 | <label for="sd_expand" class="checkbox_label" data-i18n="[title]sd_expand" title="Automatically extend prompts using text generation model"> | ||
| 31 | <input id="sd_expand" type="checkbox" /> | ||
| 32 | <span data-i18n="sd_expand_txt">Auto-extend prompts</span> | ||
| 33 | <span class="right_menu_button fa-solid fa-triangle-exclamation" data-i18n="[title]sd_expand_warning" title="May produce unexpected results. Manual prompt editing is recommended."></span> | ||
| 34 | </label> | ||
| 35 | <label for="sd_snap" class="checkbox_label" data-i18n="[title]sd_snap" title="Snap generation requests with a forced aspect ratio (portraits, backgrounds) to the nearest known resolution, while trying to preserve the absolute pixel counts (recommended for SDXL)."> | 30 | <label for="sd_snap" class="checkbox_label" data-i18n="[title]sd_snap" title="Snap generation requests with a forced aspect ratio (portraits, backgrounds) to the nearest known resolution, while trying to preserve the absolute pixel counts (recommended for SDXL)."> |
| 36 | <input id="sd_snap" type="checkbox" /> | 31 | <input id="sd_snap" type="checkbox" /> |
| 37 | <span data-i18n="sd_snap_txt">Snap auto-adjusted resolutions</span> | 32 | <span data-i18n="sd_snap_txt">Snap auto-adjusted resolutions</span> |
| @@ -308,7 +303,7 @@ | |||
| 308 | </div> | 303 | </div> |
| 309 | 304 | ||
| 310 | <div class="flex-container"> | 305 | <div class="flex-container"> |
| 311 | <div class="alignitemscenter flex-container flexFlowColumn flexGrow flexShrink gap0 flexBasis48p" data-sd-source="auto,vlad,drawthings"> | 306 | <div class="alignitemscenter flex-container flexFlowColumn flexGrow flexShrink gap0 flexBasis48p" data-sd-source="auto,vlad,drawthings,novel"> |
| 312 | <small> | 307 | <small> |
| 313 | <span data-i18n="Upscale by">Upscale by</span> | 308 | <span data-i18n="Upscale by">Upscale by</span> |
| 314 | </small> | 309 | </small> |
| @@ -332,14 +327,6 @@ | |||
| 332 | <input class="neo-range-input" type="number" id="sd_hr_second_pass_steps_value" data-for="sd_hr_second_pass_steps" max="{{hr_second_pass_steps_max}}" step="{{hr_second_pass_steps_step}}" value="{{hr_second_pass_steps}}" > | 327 | <input class="neo-range-input" type="number" id="sd_hr_second_pass_steps_value" data-for="sd_hr_second_pass_steps" max="{{hr_second_pass_steps_max}}" step="{{hr_second_pass_steps_step}}" value="{{hr_second_pass_steps}}" > |
| 333 | </div> | 328 | </div> |
| 334 | 329 | ||
| 335 | <div class="alignitemscenter flex-container flexFlowColumn flexGrow flexShrink gap0 flexBasis48p" data-sd-source="novel"> | ||
| 336 | <small> | ||
| 337 | <span data-i18n="Upscale by">Upscale by</span> | ||
| 338 | </small> | ||
| 339 | <input class="neo-range-slider" type="range" id="sd_novel_upscale_ratio" name="sd_novel_upscale_ratio" min="{{novel_upscale_ratio_min}}" max="{{novel_upscale_ratio_max}}" step="{{novel_upscale_ratio_step}}" value="{{novel_upscale_ratio}}" > | ||
| 340 | <input class="neo-range-input" type="number" id="sd_novel_upscale_ratio_value" data-for="sd_novel_upscale_ratio" min="{{novel_upscale_ratio_min}}" max="{{novel_upscale_ratio_max}}" step="{{novel_upscale_ratio_step}}" value="{{novel_upscale_ratio}}" > | ||
| 341 | </div> | ||
| 342 | |||
| 343 | <div class="alignitemscenter flex-container flexFlowColumn flexGrow flexShrink gap0 flexBasis48p" data-sd-source="auto,vlad,comfy,horde,drawthings,extras"> | 330 | <div class="alignitemscenter flex-container flexFlowColumn flexGrow flexShrink gap0 flexBasis48p" data-sd-source="auto,vlad,comfy,horde,drawthings,extras"> |
| 344 | <small> | 331 | <small> |
| 345 | <span data-i18n="CLIP Skip">CLIP Skip</span> | 332 | <span data-i18n="CLIP Skip">CLIP Skip</span> |
| @@ -724,6 +724,12 @@ async function populateChatHistory(messages, prompts, chatCompletion, type = nul | |||
| 724 | 724 | ||
| 725 | if (chatCompletion.canAfford(chatMessage)) { | 725 | if (chatCompletion.canAfford(chatMessage)) { |
| 726 | if (type === 'continue' && oai_settings.continue_prefill && chatPrompt === firstNonInjected) { | 726 | if (type === 'continue' && oai_settings.continue_prefill && chatPrompt === firstNonInjected) { |
| 727 | // in case we are using continue_prefill and the latest message is an assistant message, we want to prepend the users assistant prefill on the message | ||
| 728 | if (chatPrompt.role === 'assistant') { | ||
| 729 | const collection = new MessageCollection('continuePrefill', new Message(chatMessage.role, substituteParams(oai_settings.assistant_prefill + '\n\n') + chatMessage.content, chatMessage.identifier)); | ||
| 730 | chatCompletion.add(collection, -1); | ||
| 731 | continue; | ||
| 732 | } | ||
| 727 | const collection = new MessageCollection('continuePrefill', chatMessage); | 733 | const collection = new MessageCollection('continuePrefill', chatMessage); |
| 728 | chatCompletion.add(collection, -1); | 734 | chatCompletion.add(collection, -1); |
| 729 | continue; | 735 | continue; |
| @@ -1770,8 +1776,8 @@ async function sendOpenAIRequest(type, messages, signal) { | |||
| 1770 | generate_data['claude_use_sysprompt'] = oai_settings.claude_use_sysprompt; | 1776 | generate_data['claude_use_sysprompt'] = oai_settings.claude_use_sysprompt; |
| 1771 | generate_data['stop'] = getCustomStoppingStrings(); // Claude shouldn't have limits on stop strings. | 1777 | generate_data['stop'] = getCustomStoppingStrings(); // Claude shouldn't have limits on stop strings. |
| 1772 | generate_data['human_sysprompt_message'] = substituteParams(oai_settings.human_sysprompt_message); | 1778 | generate_data['human_sysprompt_message'] = substituteParams(oai_settings.human_sysprompt_message); |
| 1773 | // Don't add a prefill on quiet gens (summarization) | 1779 | // Don't add a prefill on quiet gens (summarization) and when using continue prefill. |
| 1774 | if (!isQuiet) { | 1780 | if (!isQuiet && !(isContinue && oai_settings.continue_prefill)) { |
| 1775 | generate_data['assistant_prefill'] = isImpersonate ? substituteParams(oai_settings.assistant_impersonation) : substituteParams(oai_settings.assistant_prefill); | 1781 | generate_data['assistant_prefill'] = isImpersonate ? substituteParams(oai_settings.assistant_impersonation) : substituteParams(oai_settings.assistant_prefill); |
| 1776 | } | 1782 | } |
| 1777 | } | 1783 | } |
| @@ -290,6 +290,7 @@ let power_user = { | |||
| 290 | restore_user_input: true, | 290 | restore_user_input: true, |
| 291 | reduced_motion: false, | 291 | reduced_motion: false, |
| 292 | compact_input_area: true, | 292 | compact_input_area: true, |
| 293 | show_swipe_num_all_messages: false, | ||
| 293 | auto_connect: false, | 294 | auto_connect: false, |
| 294 | auto_load_chat: false, | 295 | auto_load_chat: false, |
| 295 | forbid_external_media: true, | 296 | forbid_external_media: true, |
| @@ -469,6 +470,11 @@ function switchCompactInputArea() { | |||
| 469 | $('#compact_input_area').prop('checked', power_user.compact_input_area); | 470 | $('#compact_input_area').prop('checked', power_user.compact_input_area); |
| 470 | } | 471 | } |
| 471 | 472 | ||
| 473 | export function switchSwipeNumAllMessages() { | ||
| 474 | $('#show_swipe_num_all_messages').prop('checked', power_user.show_swipe_num_all_messages); | ||
| 475 | $('.mes:not(.last_mes) .swipes-counter').css('opacity', '').toggle(power_user.show_swipe_num_all_messages); | ||
| 476 | } | ||
| 477 | |||
| 472 | var originalSliderValues = []; | 478 | var originalSliderValues = []; |
| 473 | 479 | ||
| 474 | async function switchLabMode() { | 480 | async function switchLabMode() { |
| @@ -1283,6 +1289,13 @@ function applyTheme(name) { | |||
| 1283 | switchCompactInputArea(); | 1289 | switchCompactInputArea(); |
| 1284 | }, | 1290 | }, |
| 1285 | }, | 1291 | }, |
| 1292 | { | ||
| 1293 | key: 'show_swipe_num_all_messages', | ||
| 1294 | action: () => { | ||
| 1295 | $('#show_swipe_num_all_messages').prop('checked', power_user.show_swipe_num_all_messages); | ||
| 1296 | switchSwipeNumAllMessages(); | ||
| 1297 | }, | ||
| 1298 | }, | ||
| 1286 | ]; | 1299 | ]; |
| 1287 | 1300 | ||
| 1288 | for (const { key, selector, type, action } of themeProperties) { | 1301 | for (const { key, selector, type, action } of themeProperties) { |
| @@ -1352,6 +1365,7 @@ function applyPowerUserSettings() { | |||
| 1352 | switchHideChatAvatars(); | 1365 | switchHideChatAvatars(); |
| 1353 | switchTokenCount(); | 1366 | switchTokenCount(); |
| 1354 | switchMessageActions(); | 1367 | switchMessageActions(); |
| 1368 | switchSwipeNumAllMessages(); | ||
| 1355 | } | 1369 | } |
| 1356 | 1370 | ||
| 1357 | function getExampleMessagesBehavior() { | 1371 | function getExampleMessagesBehavior() { |
| @@ -2296,6 +2310,7 @@ function getThemeObject(name) { | |||
| 2296 | zoomed_avatar_magnification: power_user.zoomed_avatar_magnification, | 2310 | zoomed_avatar_magnification: power_user.zoomed_avatar_magnification, |
| 2297 | reduced_motion: power_user.reduced_motion, | 2311 | reduced_motion: power_user.reduced_motion, |
| 2298 | compact_input_area: power_user.compact_input_area, | 2312 | compact_input_area: power_user.compact_input_area, |
| 2313 | show_swipe_num_all_messages: power_user.show_swipe_num_all_messages, | ||
| 2299 | }; | 2314 | }; |
| 2300 | } | 2315 | } |
| 2301 | 2316 | ||
| @@ -3755,6 +3770,12 @@ $(document).ready(() => { | |||
| 3755 | saveSettingsDebounced(); | 3770 | saveSettingsDebounced(); |
| 3756 | }); | 3771 | }); |
| 3757 | 3772 | ||
| 3773 | $('#show_swipe_num_all_messages').on('input', function () { | ||
| 3774 | power_user.show_swipe_num_all_messages = !!$(this).prop('checked'); | ||
| 3775 | switchSwipeNumAllMessages(); | ||
| 3776 | saveSettingsDebounced(); | ||
| 3777 | }); | ||
| 3778 | |||
| 3758 | $('#auto-connect-checkbox').on('input', function () { | 3779 | $('#auto-connect-checkbox').on('input', function () { |
| 3759 | power_user.auto_connect = !!$(this).prop('checked'); | 3780 | power_user.auto_connect = !!$(this).prop('checked'); |
| 3760 | saveSettingsDebounced(); | 3781 | saveSettingsDebounced(); |
| @@ -70,6 +70,7 @@ import { POPUP_TYPE, Popup, callGenericPopup } from './popup.js'; | |||
| 70 | import { commonEnumProviders, enumIcons } from './slash-commands/SlashCommandCommonEnumsProvider.js'; | 70 | import { commonEnumProviders, enumIcons } from './slash-commands/SlashCommandCommonEnumsProvider.js'; |
| 71 | import { SlashCommandBreakController } from './slash-commands/SlashCommandBreakController.js'; | 71 | import { SlashCommandBreakController } from './slash-commands/SlashCommandBreakController.js'; |
| 72 | import { SlashCommandExecutionError } from './slash-commands/SlashCommandExecutionError.js'; | 72 | import { SlashCommandExecutionError } from './slash-commands/SlashCommandExecutionError.js'; |
| 73 | import { slashCommandReturnHelper } from './slash-commands/SlashCommandReturnHelper.js'; | ||
| 73 | export { | 74 | export { |
| 74 | executeSlashCommands, executeSlashCommandsWithOptions, getSlashCommandsHelp, registerSlashCommand, | 75 | executeSlashCommands, executeSlashCommandsWithOptions, getSlashCommandsHelp, registerSlashCommand, |
| 75 | }; | 76 | }; |
| @@ -242,6 +243,7 @@ export function initDefaultSlashCommands() { | |||
| 242 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | 243 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 243 | name: 'sendas', | 244 | name: 'sendas', |
| 244 | callback: sendMessageAs, | 245 | callback: sendMessageAs, |
| 246 | returns: 'Optionally the text of the sent message, if specified in the "return" argument', | ||
| 245 | namedArgumentList: [ | 247 | namedArgumentList: [ |
| 246 | SlashCommandNamedArgument.fromProps({ | 248 | SlashCommandNamedArgument.fromProps({ |
| 247 | name: 'name', | 249 | name: 'name', |
| @@ -269,6 +271,14 @@ export function initDefaultSlashCommands() { | |||
| 269 | typeList: [ARGUMENT_TYPE.NUMBER], | 271 | typeList: [ARGUMENT_TYPE.NUMBER], |
| 270 | enumProvider: commonEnumProviders.messages({ allowIdAfter: true }), | 272 | enumProvider: commonEnumProviders.messages({ allowIdAfter: true }), |
| 271 | }), | 273 | }), |
| 274 | SlashCommandNamedArgument.fromProps({ | ||
| 275 | name: 'return', | ||
| 276 | description: 'The way how you want the return value to be provided', | ||
| 277 | typeList: [ARGUMENT_TYPE.STRING], | ||
| 278 | defaultValue: 'none', | ||
| 279 | enumList: slashCommandReturnHelper.enumList({ allowObject: true }), | ||
| 280 | forceEnum: true, | ||
| 281 | }), | ||
| 272 | ], | 282 | ], |
| 273 | unnamedArgumentList: [ | 283 | unnamedArgumentList: [ |
| 274 | new SlashCommandArgument( | 284 | new SlashCommandArgument( |
| @@ -301,6 +311,7 @@ export function initDefaultSlashCommands() { | |||
| 301 | name: 'sys', | 311 | name: 'sys', |
| 302 | callback: sendNarratorMessage, | 312 | callback: sendNarratorMessage, |
| 303 | aliases: ['nar'], | 313 | aliases: ['nar'], |
| 314 | returns: 'Optionally the text of the sent message, if specified in the "return" argument', | ||
| 304 | namedArgumentList: [ | 315 | namedArgumentList: [ |
| 305 | new SlashCommandNamedArgument( | 316 | new SlashCommandNamedArgument( |
| 306 | 'compact', | 317 | 'compact', |
| @@ -316,6 +327,14 @@ export function initDefaultSlashCommands() { | |||
| 316 | typeList: [ARGUMENT_TYPE.NUMBER], | 327 | typeList: [ARGUMENT_TYPE.NUMBER], |
| 317 | enumProvider: commonEnumProviders.messages({ allowIdAfter: true }), | 328 | enumProvider: commonEnumProviders.messages({ allowIdAfter: true }), |
| 318 | }), | 329 | }), |
| 330 | SlashCommandNamedArgument.fromProps({ | ||
| 331 | name: 'return', | ||
| 332 | description: 'The way how you want the return value to be provided', | ||
| 333 | typeList: [ARGUMENT_TYPE.STRING], | ||
| 334 | defaultValue: 'none', | ||
| 335 | enumList: slashCommandReturnHelper.enumList({ allowObject: true }), | ||
| 336 | forceEnum: true, | ||
| 337 | }), | ||
| 319 | ], | 338 | ], |
| 320 | unnamedArgumentList: [ | 339 | unnamedArgumentList: [ |
| 321 | new SlashCommandArgument( | 340 | new SlashCommandArgument( |
| @@ -355,6 +374,7 @@ export function initDefaultSlashCommands() { | |||
| 355 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | 374 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 356 | name: 'comment', | 375 | name: 'comment', |
| 357 | callback: sendCommentMessage, | 376 | callback: sendCommentMessage, |
| 377 | returns: 'Optionally the text of the sent message, if specified in the "return" argument', | ||
| 358 | namedArgumentList: [ | 378 | namedArgumentList: [ |
| 359 | new SlashCommandNamedArgument( | 379 | new SlashCommandNamedArgument( |
| 360 | 'compact', | 380 | 'compact', |
| @@ -370,6 +390,14 @@ export function initDefaultSlashCommands() { | |||
| 370 | typeList: [ARGUMENT_TYPE.NUMBER], | 390 | typeList: [ARGUMENT_TYPE.NUMBER], |
| 371 | enumProvider: commonEnumProviders.messages({ allowIdAfter: true }), | 391 | enumProvider: commonEnumProviders.messages({ allowIdAfter: true }), |
| 372 | }), | 392 | }), |
| 393 | SlashCommandNamedArgument.fromProps({ | ||
| 394 | name: 'return', | ||
| 395 | description: 'The way how you want the return value to be provided', | ||
| 396 | typeList: [ARGUMENT_TYPE.STRING], | ||
| 397 | defaultValue: 'none', | ||
| 398 | enumList: slashCommandReturnHelper.enumList({ allowObject: true }), | ||
| 399 | forceEnum: true, | ||
| 400 | }), | ||
| 373 | ], | 401 | ], |
| 374 | unnamedArgumentList: [ | 402 | unnamedArgumentList: [ |
| 375 | new SlashCommandArgument( | 403 | new SlashCommandArgument( |
| @@ -509,7 +537,7 @@ export function initDefaultSlashCommands() { | |||
| 509 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | 537 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 510 | name: 'ask', | 538 | name: 'ask', |
| 511 | callback: askCharacter, | 539 | callback: askCharacter, |
| 512 | returns: 'the generated text', | 540 | returns: 'Optionally the text of the sent message, if specified in the "return" argument', |
| 513 | namedArgumentList: [ | 541 | namedArgumentList: [ |
| 514 | SlashCommandNamedArgument.fromProps({ | 542 | SlashCommandNamedArgument.fromProps({ |
| 515 | name: 'name', | 543 | name: 'name', |
| @@ -518,6 +546,14 @@ export function initDefaultSlashCommands() { | |||
| 518 | isRequired: true, | 546 | isRequired: true, |
| 519 | enumProvider: commonEnumProviders.characters('character'), | 547 | enumProvider: commonEnumProviders.characters('character'), |
| 520 | }), | 548 | }), |
| 549 | SlashCommandNamedArgument.fromProps({ | ||
| 550 | name: 'return', | ||
| 551 | description: 'The way how you want the return value to be provided', | ||
| 552 | typeList: [ARGUMENT_TYPE.STRING], | ||
| 553 | defaultValue: 'pipe', | ||
| 554 | enumList: slashCommandReturnHelper.enumList({ allowObject: true }), | ||
| 555 | forceEnum: true, | ||
| 556 | }), | ||
| 521 | ], | 557 | ], |
| 522 | unnamedArgumentList: [ | 558 | unnamedArgumentList: [ |
| 523 | new SlashCommandArgument( | 559 | new SlashCommandArgument( |
| @@ -556,6 +592,7 @@ export function initDefaultSlashCommands() { | |||
| 556 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | 592 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 557 | name: 'send', | 593 | name: 'send', |
| 558 | callback: sendUserMessageCallback, | 594 | callback: sendUserMessageCallback, |
| 595 | returns: 'Optionally the text of the sent message, if specified in the "return" argument', | ||
| 559 | namedArgumentList: [ | 596 | namedArgumentList: [ |
| 560 | new SlashCommandNamedArgument( | 597 | new SlashCommandNamedArgument( |
| 561 | 'compact', | 598 | 'compact', |
| @@ -578,6 +615,14 @@ export function initDefaultSlashCommands() { | |||
| 578 | defaultValue: '{{user}}', | 615 | defaultValue: '{{user}}', |
| 579 | enumProvider: commonEnumProviders.personas, | 616 | enumProvider: commonEnumProviders.personas, |
| 580 | }), | 617 | }), |
| 618 | SlashCommandNamedArgument.fromProps({ | ||
| 619 | name: 'return', | ||
| 620 | description: 'The way how you want the return value to be provided', | ||
| 621 | typeList: [ARGUMENT_TYPE.STRING], | ||
| 622 | defaultValue: 'none', | ||
| 623 | enumList: slashCommandReturnHelper.enumList({ allowObject: true }), | ||
| 624 | forceEnum: true, | ||
| 625 | }), | ||
| 581 | ], | 626 | ], |
| 582 | unnamedArgumentList: [ | 627 | unnamedArgumentList: [ |
| 583 | new SlashCommandArgument( | 628 | new SlashCommandArgument( |
| @@ -1568,12 +1613,21 @@ export function initDefaultSlashCommands() { | |||
| 1568 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | 1613 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 1569 | name: 'listinjects', | 1614 | name: 'listinjects', |
| 1570 | callback: listInjectsCallback, | 1615 | callback: listInjectsCallback, |
| 1571 | helpString: 'Lists all script injections for the current chat. Displays injects in a popup by default. Use the <code>format</code> argument to change the output format.', | 1616 | helpString: 'Lists all script injections for the current chat. Displays injects in a popup by default. Use the <code>return</code> argument to change the return type.', |
| 1572 | returns: 'JSON object of script injections', | 1617 | returns: 'Optionalls the JSON object of script injections', |
| 1573 | namedArgumentList: [ | 1618 | namedArgumentList: [ |
| 1574 | SlashCommandNamedArgument.fromProps({ | 1619 | SlashCommandNamedArgument.fromProps({ |
| 1620 | name: 'return', | ||
| 1621 | description: 'The way how you want the return value to be provided', | ||
| 1622 | typeList: [ARGUMENT_TYPE.STRING], | ||
| 1623 | defaultValue: 'popup-html', | ||
| 1624 | enumList: slashCommandReturnHelper.enumList({ allowPipe: false, allowObject: true, allowChat: true, allowPopup: true, allowTextVersion: false }), | ||
| 1625 | forceEnum: true, | ||
| 1626 | }), | ||
| 1627 | // TODO remove some day | ||
| 1628 | SlashCommandNamedArgument.fromProps({ | ||
| 1575 | name: 'format', | 1629 | name: 'format', |
| 1576 | description: 'output format', | 1630 | description: '!!! DEPRECATED - use "return" instead !!! output format', |
| 1577 | typeList: [ARGUMENT_TYPE.STRING], | 1631 | typeList: [ARGUMENT_TYPE.STRING], |
| 1578 | isRequired: true, | 1632 | isRequired: true, |
| 1579 | forceEnum: true, | 1633 | forceEnum: true, |
| @@ -1842,37 +1896,43 @@ function injectCallback(args, value) { | |||
| 1842 | } | 1896 | } |
| 1843 | 1897 | ||
| 1844 | async function listInjectsCallback(args) { | 1898 | async function listInjectsCallback(args) { |
| 1845 | const type = String(args?.format).toLowerCase().trim(); | 1899 | /** @type {import('./slash-commands/SlashCommandReturnHelper.js').SlashCommandReturnType} */ |
| 1846 | if (!chat_metadata.script_injects || !Object.keys(chat_metadata.script_injects).length) { | 1900 | let returnType = args.return; |
| 1847 | type !== 'none' && toastr.info('No script injections for the current chat'); | 1901 | |
| 1848 | return JSON.stringify({}); | 1902 | // Old legacy return type handling |
| 1849 | } | 1903 | if (args.format) { |
| 1850 | 1904 | toastr.warning(`Legacy argument 'format' with value '${args.format}' is deprecated. Please use 'return' instead. Routing to the correct return type...`, 'Deprecation warning'); | |
| 1851 | const injects = Object.entries(chat_metadata.script_injects) | 1905 | const type = String(args?.format).toLowerCase().trim(); |
| 1852 | .map(([id, inject]) => { | 1906 | if (!chat_metadata.script_injects || !Object.keys(chat_metadata.script_injects).length) { |
| 1853 | const position = Object.entries(extension_prompt_types); | 1907 | type !== 'none' && toastr.info('No script injections for the current chat'); |
| 1854 | const positionName = position.find(([_, value]) => value === inject.position)?.[0] ?? 'unknown'; | 1908 | } |
| 1855 | return `* **${id}**: <code>${inject.value}</code> (${positionName}, depth: ${inject.depth}, scan: ${inject.scan ?? false}, role: ${inject.role ?? extension_prompt_roles.SYSTEM})`; | 1909 | switch (type) { |
| 1856 | }) | 1910 | case 'none': |
| 1857 | .join('\n'); | 1911 | returnType = 'none'; |
| 1858 | 1912 | break; | |
| 1859 | const converter = new showdown.Converter(); | 1913 | case 'chat': |
| 1860 | const messageText = `### Script injections:\n${injects}`; | 1914 | returnType = 'chat-html'; |
| 1861 | const htmlMessage = DOMPurify.sanitize(converter.makeHtml(messageText)); | 1915 | break; |
| 1862 | 1916 | case 'popup': | |
| 1863 | switch (type) { | 1917 | default: |
| 1864 | case 'none': | 1918 | returnType = 'popup-html'; |
| 1865 | break; | 1919 | break; |
| 1866 | case 'chat': | 1920 | } |
| 1867 | sendSystemMessage(system_message_types.GENERIC, htmlMessage); | ||
| 1868 | break; | ||
| 1869 | case 'popup': | ||
| 1870 | default: | ||
| 1871 | await callGenericPopup(htmlMessage, POPUP_TYPE.TEXT); | ||
| 1872 | break; | ||
| 1873 | } | 1921 | } |
| 1874 | 1922 | ||
| 1875 | return JSON.stringify(chat_metadata.script_injects); | 1923 | // Now the actual new return type handling |
| 1924 | const buildTextValue = (injects) => { | ||
| 1925 | const injectsStr = Object.entries(injects) | ||
| 1926 | .map(([id, inject]) => { | ||
| 1927 | const position = Object.entries(extension_prompt_types); | ||
| 1928 | const positionName = position.find(([_, value]) => value === inject.position)?.[0] ?? 'unknown'; | ||
| 1929 | return `* **${id}**: <code>${inject.value}</code> (${positionName}, depth: ${inject.depth}, scan: ${inject.scan ?? false}, role: ${inject.role ?? extension_prompt_roles.SYSTEM})`; | ||
| 1930 | }) | ||
| 1931 | .join('\n'); | ||
| 1932 | return `### Script injections:\n${injectsStr || 'No script injections for the current chat'}`; | ||
| 1933 | }; | ||
| 1934 | |||
| 1935 | return await slashCommandReturnHelper.doReturn(returnType ?? 'popup-html', chat_metadata.script_injects ?? {}, { objectToStringFunc: buildTextValue }); | ||
| 1876 | } | 1936 | } |
| 1877 | 1937 | ||
| 1878 | /** | 1938 | /** |
| @@ -2559,7 +2619,7 @@ async function askCharacter(args, text) { | |||
| 2559 | // Not supported in group chats | 2619 | // Not supported in group chats |
| 2560 | // TODO: Maybe support group chats? | 2620 | // TODO: Maybe support group chats? |
| 2561 | if (selected_group) { | 2621 | if (selected_group) { |
| 2562 | toastr.error('Cannot run /ask command in a group chat!'); | 2622 | toastr.warning('Cannot run /ask command in a group chat!'); |
| 2563 | return ''; | 2623 | return ''; |
| 2564 | } | 2624 | } |
| 2565 | 2625 | ||
| @@ -2633,7 +2693,9 @@ async function askCharacter(args, text) { | |||
| 2633 | } | 2693 | } |
| 2634 | } | 2694 | } |
| 2635 | 2695 | ||
| 2636 | return askResult; | 2696 | const message = askResult ? chat[chat.length - 1] : null; |
| 2697 | |||
| 2698 | return await slashCommandReturnHelper.doReturn(args.return ?? 'pipe', message, { objectToStringFunc: x => x.mes }); | ||
| 2637 | } | 2699 | } |
| 2638 | 2700 | ||
| 2639 | async function hideMessageCallback(_, arg) { | 2701 | async function hideMessageCallback(_, arg) { |
| @@ -2908,7 +2970,7 @@ function findPersonaByName(name) { | |||
| 2908 | 2970 | ||
| 2909 | async function sendUserMessageCallback(args, text) { | 2971 | async function sendUserMessageCallback(args, text) { |
| 2910 | if (!text) { | 2972 | if (!text) { |
| 2911 | console.warn('WARN: No text provided for /send command'); | 2973 | toastr.warning('You must specify text to send'); |
| 2912 | return; | 2974 | return; |
| 2913 | } | 2975 | } |
| 2914 | 2976 | ||
| @@ -2924,16 +2986,17 @@ async function sendUserMessageCallback(args, text) { | |||
| 2924 | insertAt = chat.length + insertAt; | 2986 | insertAt = chat.length + insertAt; |
| 2925 | } | 2987 | } |
| 2926 | 2988 | ||
| 2989 | let message; | ||
| 2927 | if ('name' in args) { | 2990 | if ('name' in args) { |
| 2928 | const name = args.name || ''; | 2991 | const name = args.name || ''; |
| 2929 | const avatar = findPersonaByName(name) || user_avatar; | 2992 | const avatar = findPersonaByName(name) || user_avatar; |
| 2930 | await sendMessageAsUser(text, bias, insertAt, compact, name, avatar); | 2993 | message = await sendMessageAsUser(text, bias, insertAt, compact, name, avatar); |
| 2931 | } | 2994 | } |
| 2932 | else { | 2995 | else { |
| 2933 | await sendMessageAsUser(text, bias, insertAt, compact); | 2996 | message = await sendMessageAsUser(text, bias, insertAt, compact); |
| 2934 | } | 2997 | } |
| 2935 | 2998 | ||
| 2936 | return ''; | 2999 | return await slashCommandReturnHelper.doReturn(args.return ?? 'none', message, { objectToStringFunc: x => x.mes }); |
| 2937 | } | 3000 | } |
| 2938 | 3001 | ||
| 2939 | async function deleteMessagesByNameCallback(_, name) { | 3002 | async function deleteMessagesByNameCallback(_, name) { |
| @@ -3221,30 +3284,20 @@ export function getNameAndAvatarForMessage(character, name = null) { | |||
| 3221 | 3284 | ||
| 3222 | export async function sendMessageAs(args, text) { | 3285 | export async function sendMessageAs(args, text) { |
| 3223 | if (!text) { | 3286 | if (!text) { |
| 3287 | toastr.warning('You must specify text to send as'); | ||
| 3224 | return ''; | 3288 | return ''; |
| 3225 | } | 3289 | } |
| 3226 | 3290 | ||
| 3227 | let name; | 3291 | let name = args.name?.trim(); |
| 3228 | let mesText; | 3292 | let mesText; |
| 3229 | 3293 | ||
| 3230 | if (args.name) { | 3294 | if (!name) { |
| 3231 | name = args.name.trim(); | ||
| 3232 | |||
| 3233 | if (!name && !text) { | ||
| 3234 | toastr.warning('You must specify a name and text to send as'); | ||
| 3235 | return ''; | ||
| 3236 | } | ||
| 3237 | } else { | ||
| 3238 | const namelessWarningKey = 'sendAsNamelessWarningShown'; | 3295 | const namelessWarningKey = 'sendAsNamelessWarningShown'; |
| 3239 | if (localStorage.getItem(namelessWarningKey) !== 'true') { | 3296 | if (localStorage.getItem(namelessWarningKey) !== 'true') { |
| 3240 | toastr.warning('To avoid confusion, please use /sendas name="Character Name"', 'Name defaulted to {{char}}', { timeOut: 10000 }); | 3297 | toastr.warning('To avoid confusion, please use /sendas name="Character Name"', 'Name defaulted to {{char}}', { timeOut: 10000 }); |
| 3241 | localStorage.setItem(namelessWarningKey, 'true'); | 3298 | localStorage.setItem(namelessWarningKey, 'true'); |
| 3242 | } | 3299 | } |
| 3243 | name = name2; | 3300 | name = name2; |
| 3244 | if (!text) { | ||
| 3245 | toastr.warning('You must specify text to send as'); | ||
| 3246 | return ''; | ||
| 3247 | } | ||
| 3248 | } | 3301 | } |
| 3249 | 3302 | ||
| 3250 | mesText = text.trim(); | 3303 | mesText = text.trim(); |
| @@ -3321,11 +3374,12 @@ export async function sendMessageAs(args, text) { | |||
| 3321 | await saveChatConditional(); | 3374 | await saveChatConditional(); |
| 3322 | } | 3375 | } |
| 3323 | 3376 | ||
| 3324 | return ''; | 3377 | return await slashCommandReturnHelper.doReturn(args.return ?? 'none', message, { objectToStringFunc: x => x.mes }); |
| 3325 | } | 3378 | } |
| 3326 | 3379 | ||
| 3327 | export async function sendNarratorMessage(args, text) { | 3380 | export async function sendNarratorMessage(args, text) { |
| 3328 | if (!text) { | 3381 | if (!text) { |
| 3382 | toastr.warning('You must specify text to send'); | ||
| 3329 | return ''; | 3383 | return ''; |
| 3330 | } | 3384 | } |
| 3331 | 3385 | ||
| @@ -3374,7 +3428,7 @@ export async function sendNarratorMessage(args, text) { | |||
| 3374 | await saveChatConditional(); | 3428 | await saveChatConditional(); |
| 3375 | } | 3429 | } |
| 3376 | 3430 | ||
| 3377 | return ''; | 3431 | return await slashCommandReturnHelper.doReturn(args.return ?? 'none', message, { objectToStringFunc: x => x.mes }); |
| 3378 | } | 3432 | } |
| 3379 | 3433 | ||
| 3380 | export async function promptQuietForLoudResponse(who, text) { | 3434 | export async function promptQuietForLoudResponse(who, text) { |
| @@ -3420,6 +3474,7 @@ export async function promptQuietForLoudResponse(who, text) { | |||
| 3420 | 3474 | ||
| 3421 | async function sendCommentMessage(args, text) { | 3475 | async function sendCommentMessage(args, text) { |
| 3422 | if (!text) { | 3476 | if (!text) { |
| 3477 | toastr.warning('You must specify text to send'); | ||
| 3423 | return ''; | 3478 | return ''; |
| 3424 | } | 3479 | } |
| 3425 | 3480 | ||
| @@ -3462,7 +3517,7 @@ async function sendCommentMessage(args, text) { | |||
| 3462 | await saveChatConditional(); | 3517 | await saveChatConditional(); |
| 3463 | } | 3518 | } |
| 3464 | 3519 | ||
| 3465 | return ''; | 3520 | return await slashCommandReturnHelper.doReturn(args.return ?? 'none', message, { objectToStringFunc: x => x.mes }); |
| 3466 | } | 3521 | } |
| 3467 | 3522 | ||
| 3468 | /** | 3523 | /** |
| @@ -36,6 +36,7 @@ export const enumIcons = { | |||
| 36 | message: '💬', | 36 | message: '💬', |
| 37 | voice: '🎤', | 37 | voice: '🎤', |
| 38 | server: '🖥️', | 38 | server: '🖥️', |
| 39 | popup: '🗔', | ||
| 39 | 40 | ||
| 40 | true: '✔️', | 41 | true: '✔️', |
| 41 | false: '❌', | 42 | false: '❌', |
| @@ -0,0 +1,80 @@ | |||
| 1 | import { sendSystemMessage, system_message_types } from '../../script.js'; | ||
| 2 | import { callGenericPopup, POPUP_TYPE } from '../popup.js'; | ||
| 3 | import { escapeHtml } from '../utils.js'; | ||
| 4 | import { enumIcons } from './SlashCommandCommonEnumsProvider.js'; | ||
| 5 | import { enumTypes, SlashCommandEnumValue } from './SlashCommandEnumValue.js'; | ||
| 6 | |||
| 7 | /** @typedef {'pipe'|'object'|'chat-html'|'chat-text'|'popup-html'|'popup-text'|'toast-html'|'toast-text'|'console'|'none'} SlashCommandReturnType */ | ||
| 8 | |||
| 9 | export const slashCommandReturnHelper = { | ||
| 10 | // Without this, VSCode formatter fucks up JS docs. Don't ask me why. | ||
| 11 | _: false, | ||
| 12 | |||
| 13 | /** | ||
| 14 | * Gets/creates the enum list of types of return relevant for a slash command | ||
| 15 | * | ||
| 16 | * @param {object} [options={}] Options | ||
| 17 | * @param {boolean} [options.allowPipe=true] Allow option to pipe the return value | ||
| 18 | * @param {boolean} [options.allowObject=false] Allow option to return the value as an object | ||
| 19 | * @param {boolean} [options.allowChat=false] Allow option to return the value as a chat message | ||
| 20 | * @param {boolean} [options.allowPopup=false] Allow option to return the value as a popup | ||
| 21 | * @param {boolean}[options.allowTextVersion=true] Used in combination with chat/popup/toast, some of them do not make sense for text versions, e.g.if you are building a HTML string anyway | ||
| 22 | * @returns {SlashCommandEnumValue[]} The enum list | ||
| 23 | */ | ||
| 24 | enumList: ({ allowPipe = true, allowObject = false, allowChat = false, allowPopup = false, allowTextVersion = true } = {}) => [ | ||
| 25 | allowPipe && new SlashCommandEnumValue('pipe', 'Return to the pipe for the next command', enumTypes.name, '|'), | ||
| 26 | allowObject && new SlashCommandEnumValue('object', 'Return as an object (or array) to the pipe for the next command', enumTypes.variable, enumIcons.dictionary), | ||
| 27 | allowChat && new SlashCommandEnumValue('chat-html', 'Sending a chat message with the return value - Can display HTML', enumTypes.command, enumIcons.message), | ||
| 28 | allowChat && allowTextVersion && new SlashCommandEnumValue('chat-text', 'Sending a chat message with the return value - Will only display as text', enumTypes.qr, enumIcons.message), | ||
| 29 | allowPopup && new SlashCommandEnumValue('popup-html', 'Showing as a popup with the return value - Can display HTML', enumTypes.command, enumIcons.popup), | ||
| 30 | allowPopup && allowTextVersion && new SlashCommandEnumValue('popup-text', 'Showing as a popup with the return value - Will only display as text', enumTypes.qr, enumIcons.popup), | ||
| 31 | new SlashCommandEnumValue('toast-html', 'Show the return value as a toast notification - Can display HTML', enumTypes.command, 'ℹ️'), | ||
| 32 | allowTextVersion && new SlashCommandEnumValue('toast-text', 'Show the return value as a toast notification - Will only display as text', enumTypes.qr, 'ℹ️'), | ||
| 33 | new SlashCommandEnumValue('console', 'Log the return value (object, if it can be one) to the console', enumTypes.enum, '>'), | ||
| 34 | new SlashCommandEnumValue('none', 'No return value'), | ||
| 35 | ].filter(x => !!x), | ||
| 36 | |||
| 37 | /** | ||
| 38 | * Handles the return value based on the specified type | ||
| 39 | * | ||
| 40 | * @param {SlashCommandReturnType} type The type of return | ||
| 41 | * @param {object|number|string} value The value to return | ||
| 42 | * @param {object} [options={}] Options | ||
| 43 | * @param {(o: object) => string} [options.objectToStringFunc=null] Function to convert the object to a string, if object was provided and 'object' was not the chosen return type | ||
| 44 | * @param {(o: object) => string} [options.objectToHtmlFunc=null] Analog to 'objectToStringFunc', which will be used here if not provided - but can do a different string layout if HTML is requested | ||
| 45 | * @returns {Promise<*>} The processed return value | ||
| 46 | */ | ||
| 47 | async doReturn(type, value, { objectToStringFunc = o => o?.toString(), objectToHtmlFunc = null } = {}) { | ||
| 48 | const shouldHtml = type.endsWith('html'); | ||
| 49 | const actualConverterFunc = shouldHtml && objectToHtmlFunc ? objectToHtmlFunc : objectToStringFunc; | ||
| 50 | const stringValue = typeof value !== 'string' ? actualConverterFunc(value) : value; | ||
| 51 | |||
| 52 | switch (type) { | ||
| 53 | case 'popup-html': | ||
| 54 | case 'popup-text': | ||
| 55 | case 'chat-text': | ||
| 56 | case 'chat-html': | ||
| 57 | case 'toast-text': | ||
| 58 | case 'toast-html': { | ||
| 59 | const htmlOrNotHtml = shouldHtml ? DOMPurify.sanitize((new showdown.Converter()).makeHtml(stringValue)) : escapeHtml(stringValue); | ||
| 60 | |||
| 61 | if (type.startsWith('popup')) await callGenericPopup(htmlOrNotHtml, POPUP_TYPE.TEXT); | ||
| 62 | if (type.startsWith('chat')) sendSystemMessage(system_message_types.GENERIC, htmlOrNotHtml); | ||
| 63 | if (type.startsWith('toast')) toastr.info(htmlOrNotHtml, null, { escapeHtml: !shouldHtml }); | ||
| 64 | |||
| 65 | return ''; | ||
| 66 | } | ||
| 67 | case 'pipe': | ||
| 68 | return stringValue ?? ''; | ||
| 69 | case 'object': | ||
| 70 | return JSON.stringify(value); | ||
| 71 | case 'console': | ||
| 72 | console.info(value); | ||
| 73 | return ''; | ||
| 74 | case 'none': | ||
| 75 | return ''; | ||
| 76 | default: | ||
| 77 | throw new Error(`Unknown return type: ${type}`); | ||
| 78 | } | ||
| 79 | }, | ||
| 80 | }; | ||
| @@ -11,6 +11,7 @@ import { SlashCommandClosureResult } from './slash-commands/SlashCommandClosureR | |||
| 11 | import { commonEnumProviders, enumIcons } from './slash-commands/SlashCommandCommonEnumsProvider.js'; | 11 | import { commonEnumProviders, enumIcons } from './slash-commands/SlashCommandCommonEnumsProvider.js'; |
| 12 | import { SlashCommandEnumValue, enumTypes } from './slash-commands/SlashCommandEnumValue.js'; | 12 | import { SlashCommandEnumValue, enumTypes } from './slash-commands/SlashCommandEnumValue.js'; |
| 13 | import { PARSER_FLAG, SlashCommandParser } from './slash-commands/SlashCommandParser.js'; | 13 | import { PARSER_FLAG, SlashCommandParser } from './slash-commands/SlashCommandParser.js'; |
| 14 | import { slashCommandReturnHelper } from './slash-commands/SlashCommandReturnHelper.js'; | ||
| 14 | import { SlashCommandScope } from './slash-commands/SlashCommandScope.js'; | 15 | import { SlashCommandScope } from './slash-commands/SlashCommandScope.js'; |
| 15 | import { isFalseBoolean, convertValueType, isTrueBoolean } from './utils.js'; | 16 | import { isFalseBoolean, convertValueType, isTrueBoolean } from './utils.js'; |
| 16 | 17 | ||
| @@ -305,7 +306,28 @@ export function replaceVariableMacros(input) { | |||
| 305 | } | 306 | } |
| 306 | 307 | ||
| 307 | async function listVariablesCallback(args) { | 308 | async function listVariablesCallback(args) { |
| 308 | const type = String(args?.format || '').toLowerCase().trim() || 'popup'; | 309 | /** @type {import('./slash-commands/SlashCommandReturnHelper.js').SlashCommandReturnType} */ |
| 310 | let returnType = args.return; | ||
| 311 | |||
| 312 | // Old legacy return type handling | ||
| 313 | if (args.format) { | ||
| 314 | toastr.warning(`Legacy argument 'format' with value '${args.format}' is deprecated. Please use 'return' instead. Routing to the correct return type...`, 'Deprecation warning'); | ||
| 315 | const type = String(args?.format).toLowerCase().trim(); | ||
| 316 | switch (type) { | ||
| 317 | case 'none': | ||
| 318 | returnType = 'none'; | ||
| 319 | break; | ||
| 320 | case 'chat': | ||
| 321 | returnType = 'chat-html'; | ||
| 322 | break; | ||
| 323 | case 'popup': | ||
| 324 | default: | ||
| 325 | returnType = 'popup-html'; | ||
| 326 | break; | ||
| 327 | } | ||
| 328 | } | ||
| 329 | |||
| 330 | // Now the actual new return type handling | ||
| 309 | const scope = String(args?.scope || '').toLowerCase().trim() || 'all'; | 331 | const scope = String(args?.scope || '').toLowerCase().trim() || 'all'; |
| 310 | if (!chat_metadata.variables) { | 332 | if (!chat_metadata.variables) { |
| 311 | chat_metadata.variables = {}; | 333 | chat_metadata.variables = {}; |
| @@ -317,35 +339,24 @@ async function listVariablesCallback(args) { | |||
| 317 | const localVariables = includeLocalVariables ? Object.entries(chat_metadata.variables).map(([name, value]) => `${name}: ${value}`) : []; | 339 | const localVariables = includeLocalVariables ? Object.entries(chat_metadata.variables).map(([name, value]) => `${name}: ${value}`) : []; |
| 318 | const globalVariables = includeGlobalVariables ? Object.entries(extension_settings.variables.global).map(([name, value]) => `${name}: ${value}`) : []; | 340 | const globalVariables = includeGlobalVariables ? Object.entries(extension_settings.variables.global).map(([name, value]) => `${name}: ${value}`) : []; |
| 319 | 341 | ||
| 342 | const buildTextValue = (_) => { | ||
| 343 | const localVariablesString = localVariables.length > 0 ? localVariables.join('\n\n') : 'No local variables'; | ||
| 344 | const globalVariablesString = globalVariables.length > 0 ? globalVariables.join('\n\n') : 'No global variables'; | ||
| 345 | const chatName = getCurrentChatId(); | ||
| 346 | |||
| 347 | const message = [ | ||
| 348 | includeLocalVariables ? `### Local variables (${chatName}):\n${localVariablesString}` : '', | ||
| 349 | includeGlobalVariables ? `### Global variables:\n${globalVariablesString}` : '', | ||
| 350 | ].filter(x => x).join('\n\n'); | ||
| 351 | return message; | ||
| 352 | }; | ||
| 353 | |||
| 320 | const jsonVariables = [ | 354 | const jsonVariables = [ |
| 321 | ...Object.entries(chat_metadata.variables).map(x => ({ key: x[0], value: x[1], scope: 'local' })), | 355 | ...Object.entries(chat_metadata.variables).map(x => ({ key: x[0], value: x[1], scope: 'local' })), |
| 322 | ...Object.entries(extension_settings.variables.global).map(x => ({ key: x[0], value: x[1], scope: 'global' })), | 356 | ...Object.entries(extension_settings.variables.global).map(x => ({ key: x[0], value: x[1], scope: 'global' })), |
| 323 | ]; | 357 | ]; |
| 324 | 358 | ||
| 325 | const localVariablesString = localVariables.length > 0 ? localVariables.join('\n\n') : 'No local variables'; | 359 | return await slashCommandReturnHelper.doReturn(returnType ?? 'popup-html', jsonVariables, { objectToStringFunc: buildTextValue }); |
| 326 | const globalVariablesString = globalVariables.length > 0 ? globalVariables.join('\n\n') : 'No global variables'; | ||
| 327 | const chatName = getCurrentChatId(); | ||
| 328 | |||
| 329 | const converter = new showdown.Converter(); | ||
| 330 | const message = [ | ||
| 331 | includeLocalVariables ? `### Local variables (${chatName}):\n${localVariablesString}` : '', | ||
| 332 | includeGlobalVariables ? `### Global variables:\n${globalVariablesString}` : '', | ||
| 333 | ].filter(x => x).join('\n\n'); | ||
| 334 | const htmlMessage = DOMPurify.sanitize(converter.makeHtml(message)); | ||
| 335 | |||
| 336 | switch (type) { | ||
| 337 | case 'none': | ||
| 338 | break; | ||
| 339 | case 'chat': | ||
| 340 | sendSystemMessage(system_message_types.GENERIC, htmlMessage); | ||
| 341 | break; | ||
| 342 | case 'popup': | ||
| 343 | default: | ||
| 344 | await callGenericPopup(htmlMessage, POPUP_TYPE.TEXT); | ||
| 345 | break; | ||
| 346 | } | ||
| 347 | |||
| 348 | return JSON.stringify(jsonVariables); | ||
| 349 | } | 360 | } |
| 350 | 361 | ||
| 351 | /** | 362 | /** |
| @@ -916,7 +927,7 @@ export function registerVariableCommands() { | |||
| 916 | name: 'listvar', | 927 | name: 'listvar', |
| 917 | callback: listVariablesCallback, | 928 | callback: listVariablesCallback, |
| 918 | aliases: ['listchatvar'], | 929 | aliases: ['listchatvar'], |
| 919 | helpString: 'List registered chat variables. Displays variables in a popup by default. Use the <code>format</code> argument to change the output format.', | 930 | helpString: 'List registered chat variables. Displays variables in a popup by default. Use the <code>return</code> argument to change the return type.', |
| 920 | returns: 'JSON list of local variables', | 931 | returns: 'JSON list of local variables', |
| 921 | namedArgumentList: [ | 932 | namedArgumentList: [ |
| 922 | SlashCommandNamedArgument.fromProps({ | 933 | SlashCommandNamedArgument.fromProps({ |
| @@ -933,8 +944,17 @@ export function registerVariableCommands() { | |||
| 933 | ], | 944 | ], |
| 934 | }), | 945 | }), |
| 935 | SlashCommandNamedArgument.fromProps({ | 946 | SlashCommandNamedArgument.fromProps({ |
| 947 | name: 'return', | ||
| 948 | description: 'The way how you want the return value to be provided', | ||
| 949 | typeList: [ARGUMENT_TYPE.STRING], | ||
| 950 | defaultValue: 'popup-html', | ||
| 951 | enumList: slashCommandReturnHelper.enumList({ allowPipe: false, allowObject: true, allowChat: true, allowPopup: true, allowTextVersion: false }), | ||
| 952 | forceEnum: true, | ||
| 953 | }), | ||
| 954 | // TODO remove some day | ||
| 955 | SlashCommandNamedArgument.fromProps({ | ||
| 936 | name: 'format', | 956 | name: 'format', |
| 937 | description: 'output format', | 957 | description: '!!! DEPRECATED - use "return" instead !!! output format', |
| 938 | typeList: [ARGUMENT_TYPE.STRING], | 958 | typeList: [ARGUMENT_TYPE.STRING], |
| 939 | isRequired: true, | 959 | isRequired: true, |
| 940 | forceEnum: true, | 960 | forceEnum: true, |
| @@ -969,34 +969,53 @@ body .panelControlBar { | |||
| 969 | 969 | ||
| 970 | /* SWIPE RELATED STYLES*/ | 970 | /* SWIPE RELATED STYLES*/ |
| 971 | 971 | ||
| 972 | .mes { | ||
| 973 | --swipeCounterHeight: 15px; | ||
| 974 | --swipeCounterMargin: 5px; | ||
| 975 | } | ||
| 976 | |||
| 972 | .swipe_right, | 977 | .swipe_right, |
| 973 | .swipe_left { | 978 | .swipe_left { |
| 974 | height: 40px; | 979 | width: 25px; |
| 975 | width: 40px; | 980 | height: 25px; |
| 976 | opacity: 0.3; | 981 | opacity: 0.3; |
| 977 | align-items: center; | 982 | align-items: center; |
| 978 | justify-content: center; | 983 | justify-content: center; |
| 979 | z-index: 9999; | 984 | z-index: 9999; |
| 980 | grid-row-start: 2; | 985 | grid-row-start: 2; |
| 981 | font-size: 30px; | 986 | font-size: 20px; |
| 982 | cursor: pointer; | 987 | cursor: pointer; |
| 983 | align-self: center; | 988 | align-self: center; |
| 989 | } | ||
| 990 | |||
| 991 | .swipe_left { | ||
| 984 | position: absolute; | 992 | position: absolute; |
| 985 | bottom: 15px; | 993 | bottom: calc(var(--swipeCounterHeight) + var(--swipeCounterMargin)); |
| 986 | flex-flow: column; | 994 | flex-flow: column; |
| 987 | } | 995 | } |
| 988 | 996 | ||
| 997 | .swipeRightBlock { | ||
| 998 | position: absolute; | ||
| 999 | right: 0; | ||
| 1000 | bottom: 0; | ||
| 1001 | } | ||
| 989 | 1002 | ||
| 990 | .swipes-counter { | 1003 | .swipes-counter { |
| 991 | color: var(--SmartThemeBodyColor); | 1004 | color: var(--SmartThemeBodyColor); |
| 992 | font-size: 12px; | 1005 | font-size: 12px; |
| 993 | padding: 0; | 1006 | padding: 0 5px; |
| 994 | font-family: var(--mainFontFamily); | 1007 | font-family: var(--mainFontFamily); |
| 995 | font-weight: 400; | 1008 | font-weight: 400; |
| 996 | align-self: center; | 1009 | align-self: center; |
| 997 | min-width: 40px; | 1010 | min-width: 40px; |
| 998 | display: flex; | 1011 | display: flex; |
| 999 | justify-content: center; | 1012 | justify-content: center; |
| 1013 | margin-bottom: var(--swipeCounterMargin); | ||
| 1014 | height: var(--swipeCounterHeight); | ||
| 1015 | } | ||
| 1016 | |||
| 1017 | .mes:not(.last_mes) .swipes-counter { | ||
| 1018 | opacity: 0.3; | ||
| 1000 | } | 1019 | } |
| 1001 | 1020 | ||
| 1002 | .swipe_left { | 1021 | .swipe_left { |
| @@ -1006,7 +1025,7 @@ flex-flow: column; | |||
| 1006 | 1025 | ||
| 1007 | .swipe_right { | 1026 | .swipe_right { |
| 1008 | right: 5px; | 1027 | right: 5px; |
| 1009 | align-self:end; | 1028 | align-self: center; |
| 1010 | } | 1029 | } |
| 1011 | 1030 | ||
| 1012 | .ui-settings { | 1031 | .ui-settings { |
| @@ -2640,7 +2659,7 @@ select option:not(:checked) { | |||
| 2640 | 2659 | ||
| 2641 | #instruct_enabled_label .menu_button:not(.toggleEnabled), | 2660 | #instruct_enabled_label .menu_button:not(.toggleEnabled), |
| 2642 | #sysprompt_enabled_label .menu_button:not(.toggleEnabled) { | 2661 | #sysprompt_enabled_label .menu_button:not(.toggleEnabled) { |
| 2643 | color: Red; | 2662 | color: Red; |
| 2644 | } | 2663 | } |
| 2645 | 2664 | ||
| 2646 | .displayBlock { | 2665 | .displayBlock { |
| @@ -10,41 +10,6 @@ const { readSecret, SECRET_KEYS } = require('./secrets.js'); | |||
| 10 | const FormData = require('form-data'); | 10 | const FormData = require('form-data'); |
| 11 | 11 | ||
| 12 | /** | 12 | /** |
| 13 | * Sanitizes a string. | ||
| 14 | * @param {string} x String to sanitize | ||
| 15 | * @returns {string} Sanitized string | ||
| 16 | */ | ||
| 17 | function safeStr(x) { | ||
| 18 | x = String(x); | ||
| 19 | x = x.replace(/ +/g, ' '); | ||
| 20 | x = x.trim(); | ||
| 21 | x = x.replace(/^[\s,.]+|[\s,.]+$/g, ''); | ||
| 22 | return x; | ||
| 23 | } | ||
| 24 | |||
| 25 | const splitStrings = [ | ||
| 26 | ', extremely', | ||
| 27 | ', intricate,', | ||
| 28 | ]; | ||
| 29 | |||
| 30 | const dangerousPatterns = '[]【】()()|::'; | ||
| 31 | |||
| 32 | /** | ||
| 33 | * Removes patterns from a string. | ||
| 34 | * @param {string} x String to sanitize | ||
| 35 | * @param {string} pattern Pattern to remove | ||
| 36 | * @returns {string} Sanitized string | ||
| 37 | */ | ||
| 38 | function removePattern(x, pattern) { | ||
| 39 | for (let i = 0; i < pattern.length; i++) { | ||
| 40 | let p = pattern[i]; | ||
| 41 | let regex = new RegExp('\\' + p, 'g'); | ||
| 42 | x = x.replace(regex, ''); | ||
| 43 | } | ||
| 44 | return x; | ||
| 45 | } | ||
| 46 | |||
| 47 | /** | ||
| 48 | * Gets the comfy workflows. | 13 | * Gets the comfy workflows. |
| 49 | * @param {import('../users.js').UserDirectoryList} directories | 14 | * @param {import('../users.js').UserDirectoryList} directories |
| 50 | * @returns {string[]} List of comfy workflows | 15 | * @returns {string[]} List of comfy workflows |
| @@ -391,40 +356,6 @@ router.post('/sd-next/upscalers', jsonParser, async (request, response) => { | |||
| 391 | } | 356 | } |
| 392 | }); | 357 | }); |
| 393 | 358 | ||
| 394 | /** | ||
| 395 | * SD prompt expansion using GPT-2 text generation model. | ||
| 396 | * Adapted from: https://github.com/lllyasviel/Fooocus/blob/main/modules/expansion.py | ||
| 397 | */ | ||
| 398 | router.post('/expand', jsonParser, async (request, response) => { | ||
| 399 | const originalPrompt = request.body.prompt; | ||
| 400 | |||
| 401 | if (!originalPrompt) { | ||
| 402 | console.warn('No prompt provided for SD expansion.'); | ||
| 403 | return response.send({ prompt: '' }); | ||
| 404 | } | ||
| 405 | |||
| 406 | console.log('Refine prompt input:', originalPrompt); | ||
| 407 | const splitString = splitStrings[Math.floor(Math.random() * splitStrings.length)]; | ||
| 408 | let prompt = safeStr(originalPrompt) + splitString; | ||
| 409 | |||
| 410 | try { | ||
| 411 | const task = 'text-generation'; | ||
| 412 | const module = await import('../transformers.mjs'); | ||
| 413 | const pipe = await module.default.getPipeline(task); | ||
| 414 | |||
| 415 | const result = await pipe(prompt, { num_beams: 1, max_new_tokens: 256, do_sample: true }); | ||
| 416 | |||
| 417 | const newText = result[0].generated_text; | ||
| 418 | const newPrompt = safeStr(removePattern(newText, dangerousPatterns)); | ||
| 419 | console.log('Refine prompt output:', newPrompt); | ||
| 420 | |||
| 421 | return response.send({ prompt: newPrompt }); | ||
| 422 | } catch { | ||
| 423 | console.warn('Failed to load transformers.js pipeline.'); | ||
| 424 | return response.send({ prompt: originalPrompt }); | ||
| 425 | } | ||
| 426 | }); | ||
| 427 | |||
| 428 | const comfy = express.Router(); | 359 | const comfy = express.Router(); |
| 429 | 360 | ||
| 430 | comfy.post('/ping', jsonParser, async (request, response) => { | 361 | comfy.post('/ping', jsonParser, async (request, response) => { |
| @@ -31,12 +31,6 @@ const tasks = { | |||
| 31 | configField: 'extras.embeddingModel', | 31 | configField: 'extras.embeddingModel', |
| 32 | quantized: true, | 32 | quantized: true, |
| 33 | }, | 33 | }, |
| 34 | 'text-generation': { | ||
| 35 | defaultModel: 'Cohee/fooocus_expansion-onnx', | ||
| 36 | pipeline: null, | ||
| 37 | configField: 'extras.promptExpansionModel', | ||
| 38 | quantized: false, | ||
| 39 | }, | ||
| 40 | 'automatic-speech-recognition': { | 34 | 'automatic-speech-recognition': { |
| 41 | defaultModel: 'Xenova/whisper-small', | 35 | defaultModel: 'Xenova/whisper-small', |
| 42 | pipeline: null, | 36 | pipeline: null, |