Move default slash commands to respective module (#4240) * Move default slash commands to respective module * Update public/scripts/slash-commands.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Signed| @@ -1,7 +1,6 @@ | |||
| 1 | import { | 1 | import { |
| 2 | showdown, | 2 | showdown, |
| 3 | moment, | 3 | moment, |
| 4 | Fuse, | ||
| 5 | DOMPurify, | 4 | DOMPurify, |
| 6 | hljs, | 5 | hljs, |
| 7 | Handlebars, | 6 | Handlebars, |
| @@ -89,7 +88,6 @@ import { | |||
| 89 | sortEntitiesList, | 88 | sortEntitiesList, |
| 90 | registerDebugFunction, | 89 | registerDebugFunction, |
| 91 | flushEphemeralStoppingStrings, | 90 | flushEphemeralStoppingStrings, |
| 92 | context_presets, | ||
| 93 | resetMovableStyles, | 91 | resetMovableStyles, |
| 94 | forceCharacterEditorTokenize, | 92 | forceCharacterEditorTokenize, |
| 95 | applyPowerUserSettings, | 93 | applyPowerUserSettings, |
| @@ -167,7 +165,6 @@ import { | |||
| 167 | isValidUrl, | 165 | isValidUrl, |
| 168 | ensureImageFormatSupported, | 166 | ensureImageFormatSupported, |
| 169 | flashHighlight, | 167 | flashHighlight, |
| 170 | isTrueBoolean, | ||
| 171 | toggleDrawer, | 168 | toggleDrawer, |
| 172 | isElementInViewport, | 169 | isElementInViewport, |
| 173 | copyText, | 170 | copyText, |
| @@ -182,7 +179,7 @@ import { | |||
| 182 | import { debounce_timeout, IGNORE_SYMBOL } from './scripts/constants.js'; | 179 | import { debounce_timeout, IGNORE_SYMBOL } from './scripts/constants.js'; |
| 183 | 180 | ||
| 184 | import { cancelDebouncedMetadataSave, doDailyExtensionUpdatesCheck, extension_settings, initExtensions, loadExtensionSettings, runGenerationInterceptors, saveMetadataDebounced } from './scripts/extensions.js'; | 181 | import { cancelDebouncedMetadataSave, doDailyExtensionUpdatesCheck, extension_settings, initExtensions, loadExtensionSettings, runGenerationInterceptors, saveMetadataDebounced } from './scripts/extensions.js'; |
| 185 | import { COMMENT_NAME_DEFAULT, executeSlashCommandsOnChatInput, getSlashCommandsHelp, initDefaultSlashCommands, isExecutingCommandsFromChatInput, pauseScriptExecution, processChatSlashCommands, stopScriptExecution } from './scripts/slash-commands.js'; | 182 | import { COMMENT_NAME_DEFAULT, CONNECT_API_MAP, executeSlashCommandsOnChatInput, getSlashCommandsHelp, initDefaultSlashCommands, isExecutingCommandsFromChatInput, pauseScriptExecution, processChatSlashCommands, stopScriptExecution, UNIQUE_APIS } from './scripts/slash-commands.js'; |
| 186 | import { | 183 | import { |
| 187 | tag_map, | 184 | tag_map, |
| 188 | tags, | 185 | tags, |
| @@ -223,9 +220,6 @@ import { | |||
| 223 | formatInstructModeExamples, | 220 | formatInstructModeExamples, |
| 224 | getInstructStoppingSequences, | 221 | getInstructStoppingSequences, |
| 225 | formatInstructModeSystemPrompt, | 222 | formatInstructModeSystemPrompt, |
| 226 | selectInstructPreset, | ||
| 227 | instruct_presets, | ||
| 228 | selectContextPreset, | ||
| 229 | } from './scripts/instruct-mode.js'; | 223 | } from './scripts/instruct-mode.js'; |
| 230 | import { initLocales, t } from './scripts/i18n.js'; | 224 | import { initLocales, t } from './scripts/i18n.js'; |
| 231 | import { getFriendlyTokenizerName, getTokenCount, getTokenCountAsync, initTokenizers, saveTokenCache } from './scripts/tokenizers.js'; | 225 | import { getFriendlyTokenizerName, getTokenCount, getTokenCountAsync, initTokenizers, saveTokenCache } from './scripts/tokenizers.js'; |
| @@ -251,16 +245,11 @@ import { currentUser, setUserControls } from './scripts/user.js'; | |||
| 251 | import { POPUP_RESULT, POPUP_TYPE, Popup, callGenericPopup, fixToastrForDialogs } from './scripts/popup.js'; | 245 | import { POPUP_RESULT, POPUP_TYPE, Popup, callGenericPopup, fixToastrForDialogs } from './scripts/popup.js'; |
| 252 | import { renderTemplate, renderTemplateAsync } from './scripts/templates.js'; | 246 | import { renderTemplate, renderTemplateAsync } from './scripts/templates.js'; |
| 253 | import { initScrapers } from './scripts/scrapers.js'; | 247 | import { initScrapers } from './scripts/scrapers.js'; |
| 254 | import { SlashCommandParser } from './scripts/slash-commands/SlashCommandParser.js'; | ||
| 255 | import { SlashCommand } from './scripts/slash-commands/SlashCommand.js'; | ||
| 256 | import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from './scripts/slash-commands/SlashCommandArgument.js'; | ||
| 257 | import { SlashCommandBrowser } from './scripts/slash-commands/SlashCommandBrowser.js'; | 248 | import { SlashCommandBrowser } from './scripts/slash-commands/SlashCommandBrowser.js'; |
| 258 | import { initCustomSelectedSamplers, validateDisabledSamplers } from './scripts/samplerSelect.js'; | 249 | import { initCustomSelectedSamplers, validateDisabledSamplers } from './scripts/samplerSelect.js'; |
| 259 | import { DragAndDropHandler } from './scripts/dragdrop.js'; | 250 | import { DragAndDropHandler } from './scripts/dragdrop.js'; |
| 260 | import { INTERACTABLE_CONTROL_CLASS, initKeyboard } from './scripts/keyboard.js'; | 251 | import { INTERACTABLE_CONTROL_CLASS, initKeyboard } from './scripts/keyboard.js'; |
| 261 | import { initDynamicStyles } from './scripts/dynamic-styles.js'; | 252 | import { initDynamicStyles } from './scripts/dynamic-styles.js'; |
| 262 | import { SlashCommandEnumValue, enumTypes } from './scripts/slash-commands/SlashCommandEnumValue.js'; | ||
| 263 | import { commonEnumProviders, enumIcons } from './scripts/slash-commands/SlashCommandCommonEnumsProvider.js'; | ||
| 264 | import { initInputMarkdown } from './scripts/input-md-formatting.js'; | 253 | import { initInputMarkdown } from './scripts/input-md-formatting.js'; |
| 265 | import { AbortReason } from './scripts/util/AbortReason.js'; | 254 | import { AbortReason } from './scripts/util/AbortReason.js'; |
| 266 | import { initSystemPrompts } from './scripts/sysprompt.js'; | 255 | import { initSystemPrompts } from './scripts/sysprompt.js'; |
| @@ -306,6 +295,8 @@ export { | |||
| 306 | koboldai_setting_names, | 295 | koboldai_setting_names, |
| 307 | novelai_settings, | 296 | novelai_settings, |
| 308 | novelai_setting_names, | 297 | novelai_setting_names, |
| 298 | UNIQUE_APIS, | ||
| 299 | CONNECT_API_MAP, | ||
| 309 | }; | 300 | }; |
| 310 | 301 | ||
| 311 | /** | 302 | /** |
| @@ -5257,7 +5248,7 @@ function addChatsSeparator(mesSendString) { | |||
| 5257 | } | 5248 | } |
| 5258 | } | 5249 | } |
| 5259 | 5250 | ||
| 5260 | async function duplicateCharacter() { | 5251 | export async function duplicateCharacter() { |
| 5261 | if (this_chid === undefined || !characters[this_chid]) { | 5252 | if (this_chid === undefined || !characters[this_chid]) { |
| 5262 | toastr.warning(t`You must first select a character to duplicate!`); | 5253 | toastr.warning(t`You must first select a character to duplicate!`); |
| 5263 | return ''; | 5254 | return ''; |
| @@ -7385,7 +7376,7 @@ export async function getPastCharacterChats(characterId = null) { | |||
| 7385 | /** | 7376 | /** |
| 7386 | * Helper for `displayPastChats`, to make the same info consistently available for other functions | 7377 | * Helper for `displayPastChats`, to make the same info consistently available for other functions |
| 7387 | */ | 7378 | */ |
| 7388 | function getCurrentChatDetails() { | 7379 | export function getCurrentChatDetails() { |
| 7389 | if (!characters[this_chid] && !selected_group) { | 7380 | if (!characters[this_chid] && !selected_group) { |
| 7390 | return { sessionName: '', group: null, characterName: '', avatarImgURL: '' }; | 7381 | return { sessionName: '', group: null, characterName: '', avatarImgURL: '' }; |
| 7391 | } | 7382 | } |
| @@ -8931,219 +8922,6 @@ export function swipe_right(_event = null, { source, repeated } = {}) { | |||
| 8931 | } | 8922 | } |
| 8932 | 8923 | ||
| 8933 | /** | 8924 | /** |
| 8934 | * @typedef {object} ConnectAPIMap | ||
| 8935 | * @property {string} selected - API name (e.g. "textgenerationwebui", "openai") | ||
| 8936 | * @property {string?} [button] - CSS selector for the API button | ||
| 8937 | * @property {string?} [type] - API type, mostly used by text completion. (e.g. "openrouter") | ||
| 8938 | * @property {string?} [source] - API source, mostly used by chat completion. (e.g. "openai") | ||
| 8939 | */ | ||
| 8940 | |||
| 8941 | /** | ||
| 8942 | * @type {Record<string, ConnectAPIMap>} | ||
| 8943 | */ | ||
| 8944 | export const CONNECT_API_MAP = { | ||
| 8945 | // Default APIs not contined inside text gen / chat gen | ||
| 8946 | 'kobold': { | ||
| 8947 | selected: 'kobold', | ||
| 8948 | button: '#api_button', | ||
| 8949 | }, | ||
| 8950 | 'horde': { | ||
| 8951 | selected: 'koboldhorde', | ||
| 8952 | }, | ||
| 8953 | 'novel': { | ||
| 8954 | selected: 'novel', | ||
| 8955 | button: '#api_button_novel', | ||
| 8956 | }, | ||
| 8957 | 'koboldcpp': { | ||
| 8958 | selected: 'textgenerationwebui', | ||
| 8959 | button: '#api_button_textgenerationwebui', | ||
| 8960 | type: textgen_types.KOBOLDCPP, | ||
| 8961 | }, | ||
| 8962 | // KoboldCpp alias | ||
| 8963 | 'kcpp': { | ||
| 8964 | selected: 'textgenerationwebui', | ||
| 8965 | button: '#api_button_textgenerationwebui', | ||
| 8966 | type: textgen_types.KOBOLDCPP, | ||
| 8967 | }, | ||
| 8968 | 'openai': { | ||
| 8969 | selected: 'openai', | ||
| 8970 | button: '#api_button_openai', | ||
| 8971 | source: chat_completion_sources.OPENAI, | ||
| 8972 | }, | ||
| 8973 | // OpenAI alias | ||
| 8974 | 'oai': { | ||
| 8975 | selected: 'openai', | ||
| 8976 | button: '#api_button_openai', | ||
| 8977 | source: chat_completion_sources.OPENAI, | ||
| 8978 | }, | ||
| 8979 | // Google alias | ||
| 8980 | 'google': { | ||
| 8981 | selected: 'openai', | ||
| 8982 | button: '#api_button_openai', | ||
| 8983 | source: chat_completion_sources.MAKERSUITE, | ||
| 8984 | }, | ||
| 8985 | // OpenRouter special naming, to differentiate between chat comp and text comp | ||
| 8986 | 'openrouter': { | ||
| 8987 | selected: 'openai', | ||
| 8988 | button: '#api_button_openai', | ||
| 8989 | source: chat_completion_sources.OPENROUTER, | ||
| 8990 | }, | ||
| 8991 | 'openrouter-text': { | ||
| 8992 | selected: 'textgenerationwebui', | ||
| 8993 | button: '#api_button_textgenerationwebui', | ||
| 8994 | type: textgen_types.OPENROUTER, | ||
| 8995 | }, | ||
| 8996 | }; | ||
| 8997 | |||
| 8998 | // Collect all unique API names in an array | ||
| 8999 | export const UNIQUE_APIS = [...new Set(Object.values(CONNECT_API_MAP).map(x => x.selected))]; | ||
| 9000 | |||
| 9001 | // Fill connections map from textgen_types and chat_completion_sources | ||
| 9002 | for (const textGenType of Object.values(textgen_types)) { | ||
| 9003 | if (CONNECT_API_MAP[textGenType]) continue; | ||
| 9004 | CONNECT_API_MAP[textGenType] = { | ||
| 9005 | selected: 'textgenerationwebui', | ||
| 9006 | button: '#api_button_textgenerationwebui', | ||
| 9007 | type: textGenType, | ||
| 9008 | }; | ||
| 9009 | } | ||
| 9010 | for (const chatCompletionSource of Object.values(chat_completion_sources)) { | ||
| 9011 | if (CONNECT_API_MAP[chatCompletionSource]) continue; | ||
| 9012 | CONNECT_API_MAP[chatCompletionSource] = { | ||
| 9013 | selected: 'openai', | ||
| 9014 | button: '#api_button_openai', | ||
| 9015 | source: chatCompletionSource, | ||
| 9016 | }; | ||
| 9017 | } | ||
| 9018 | |||
| 9019 | async function selectContextCallback(args, name) { | ||
| 9020 | if (!name) { | ||
| 9021 | return power_user.context.preset; | ||
| 9022 | } | ||
| 9023 | |||
| 9024 | const quiet = isTrueBoolean(args?.quiet); | ||
| 9025 | const contextNames = context_presets.map(preset => preset.name); | ||
| 9026 | const fuse = new Fuse(contextNames); | ||
| 9027 | const result = fuse.search(name); | ||
| 9028 | |||
| 9029 | if (result.length === 0) { | ||
| 9030 | !quiet && toastr.warning(t`Context template '${name}' not found`); | ||
| 9031 | return ''; | ||
| 9032 | } | ||
| 9033 | |||
| 9034 | const foundName = result[0].item; | ||
| 9035 | selectContextPreset(foundName, { quiet: quiet }); | ||
| 9036 | return foundName; | ||
| 9037 | } | ||
| 9038 | |||
| 9039 | async function selectInstructCallback(args, name) { | ||
| 9040 | if (!name) { | ||
| 9041 | return power_user.instruct.enabled || isTrueBoolean(args?.forceGet) ? power_user.instruct.preset : ''; | ||
| 9042 | } | ||
| 9043 | |||
| 9044 | const quiet = isTrueBoolean(args?.quiet); | ||
| 9045 | const instructNames = instruct_presets.map(preset => preset.name); | ||
| 9046 | const fuse = new Fuse(instructNames); | ||
| 9047 | const result = fuse.search(name); | ||
| 9048 | |||
| 9049 | if (result.length === 0) { | ||
| 9050 | !quiet && toastr.warning(t`Instruct template '${name}' not found`); | ||
| 9051 | return ''; | ||
| 9052 | } | ||
| 9053 | |||
| 9054 | const foundName = result[0].item; | ||
| 9055 | selectInstructPreset(foundName, { quiet: quiet }); | ||
| 9056 | return foundName; | ||
| 9057 | } | ||
| 9058 | |||
| 9059 | async function enableInstructCallback() { | ||
| 9060 | $('#instruct_enabled').prop('checked', true).trigger('input').trigger('change'); | ||
| 9061 | return ''; | ||
| 9062 | } | ||
| 9063 | |||
| 9064 | async function disableInstructCallback() { | ||
| 9065 | $('#instruct_enabled').prop('checked', false).trigger('input').trigger('change'); | ||
| 9066 | return ''; | ||
| 9067 | } | ||
| 9068 | |||
| 9069 | /** | ||
| 9070 | * @param {string} text API name | ||
| 9071 | */ | ||
| 9072 | async function connectAPISlash(args, text) { | ||
| 9073 | if (!text.trim()) { | ||
| 9074 | for (const [key, config] of Object.entries(CONNECT_API_MAP)) { | ||
| 9075 | if (config.selected !== main_api) continue; | ||
| 9076 | |||
| 9077 | if (config.source) { | ||
| 9078 | if (oai_settings.chat_completion_source === config.source) { | ||
| 9079 | return key; | ||
| 9080 | } else { | ||
| 9081 | continue; | ||
| 9082 | } | ||
| 9083 | } | ||
| 9084 | |||
| 9085 | if (config.type) { | ||
| 9086 | if (textgen_settings.type === config.type) { | ||
| 9087 | return key; | ||
| 9088 | } else { | ||
| 9089 | continue; | ||
| 9090 | } | ||
| 9091 | } | ||
| 9092 | |||
| 9093 | return key; | ||
| 9094 | } | ||
| 9095 | |||
| 9096 | console.error('FIXME: The current API is not in the API map'); | ||
| 9097 | return ''; | ||
| 9098 | } | ||
| 9099 | |||
| 9100 | const apiConfig = CONNECT_API_MAP[text.toLowerCase()]; | ||
| 9101 | if (!apiConfig) { | ||
| 9102 | toastr.error(t`Error: ${text} is not a valid API`); | ||
| 9103 | return ''; | ||
| 9104 | } | ||
| 9105 | |||
| 9106 | let connectionRequired = false; | ||
| 9107 | |||
| 9108 | if (main_api !== apiConfig.selected) { | ||
| 9109 | $(`#main_api option[value='${apiConfig.selected || text}']`).prop('selected', true); | ||
| 9110 | $('#main_api').trigger('change'); | ||
| 9111 | connectionRequired = true; | ||
| 9112 | } | ||
| 9113 | |||
| 9114 | if (apiConfig.source && oai_settings.chat_completion_source !== apiConfig.source) { | ||
| 9115 | $(`#chat_completion_source option[value='${apiConfig.source}']`).prop('selected', true); | ||
| 9116 | $('#chat_completion_source').trigger('change'); | ||
| 9117 | connectionRequired = true; | ||
| 9118 | } | ||
| 9119 | |||
| 9120 | if (apiConfig.type && textgen_settings.type !== apiConfig.type) { | ||
| 9121 | $(`#textgen_type option[value='${apiConfig.type}']`).prop('selected', true); | ||
| 9122 | $('#textgen_type').trigger('change'); | ||
| 9123 | connectionRequired = true; | ||
| 9124 | } | ||
| 9125 | |||
| 9126 | if (connectionRequired && apiConfig.button) { | ||
| 9127 | $(apiConfig.button).trigger('click'); | ||
| 9128 | } | ||
| 9129 | |||
| 9130 | const quiet = isTrueBoolean(args?.quiet); | ||
| 9131 | const toast = quiet ? jQuery() : toastr.info(t`API set to ${text}, trying to connect..`); | ||
| 9132 | |||
| 9133 | try { | ||
| 9134 | if (connectionRequired) { | ||
| 9135 | await waitUntilCondition(() => online_status !== 'no_connection', 5000, 100); | ||
| 9136 | } | ||
| 9137 | console.log('Connection successful'); | ||
| 9138 | } catch { | ||
| 9139 | console.log('Could not connect after 5 seconds, skipping.'); | ||
| 9140 | } | ||
| 9141 | |||
| 9142 | toastr.clear(toast); | ||
| 9143 | return text; | ||
| 9144 | } | ||
| 9145 | |||
| 9146 | /** | ||
| 9147 | * Imports supported files dropped into the app window. | 8925 | * Imports supported files dropped into the app window. |
| 9148 | * @param {File[]} files Array of files to process | 8926 | * @param {File[]} files Array of files to process |
| 9149 | * @param {Map<File, string>} [data] Extra data to pass to the import function | 8927 | * @param {Map<File, string>} [data] Extra data to pass to the import function |
| @@ -9286,32 +9064,6 @@ async function importFromURL(items, files) { | |||
| 9286 | } | 9064 | } |
| 9287 | } | 9065 | } |
| 9288 | 9066 | ||
| 9289 | async function doImpersonate(args, prompt) { | ||
| 9290 | const options = prompt?.trim() ? { quiet_prompt: prompt.trim(), quietToLoud: true } : {}; | ||
| 9291 | const shouldAwait = isTrueBoolean(args?.await); | ||
| 9292 | const outerPromise = new Promise((outerResolve) => setTimeout(async () => { | ||
| 9293 | try { | ||
| 9294 | await waitUntilCondition(() => !is_send_press && !is_group_generating, 10000, 100); | ||
| 9295 | } catch { | ||
| 9296 | console.warn('Timeout waiting for generation unlock'); | ||
| 9297 | toastr.warning(t`Cannot run /impersonate command while the reply is being generated.`); | ||
| 9298 | return ''; | ||
| 9299 | } | ||
| 9300 | |||
| 9301 | // Prevent generate recursion | ||
| 9302 | $('#send_textarea').val('')[0].dispatchEvent(new Event('input', { bubbles: true })); | ||
| 9303 | |||
| 9304 | outerResolve(new Promise(innerResolve => setTimeout(() => innerResolve(Generate('impersonate', options)), 1))); | ||
| 9305 | }, 1)); | ||
| 9306 | |||
| 9307 | if (shouldAwait) { | ||
| 9308 | const innerPromise = await outerPromise; | ||
| 9309 | await innerPromise; | ||
| 9310 | } | ||
| 9311 | |||
| 9312 | return ''; | ||
| 9313 | } | ||
| 9314 | |||
| 9315 | export async function doNewChat({ deleteCurrentChat = false } = {}) { | 9067 | export async function doNewChat({ deleteCurrentChat = false } = {}) { |
| 9316 | //Make a new chat for selected character | 9068 | //Make a new chat for selected character |
| 9317 | if ((!selected_group && this_chid == undefined) || menu_type == 'create') { | 9069 | if ((!selected_group && this_chid == undefined) || menu_type == 'create') { |
| @@ -9346,53 +9098,6 @@ export async function doNewChat({ deleteCurrentChat = false } = {}) { | |||
| 9346 | 9098 | ||
| 9347 | } | 9099 | } |
| 9348 | 9100 | ||
| 9349 | async function doDeleteChat() { | ||
| 9350 | return displayPastChats().then(() => new Promise((resolve) => { | ||
| 9351 | let resolved = false; | ||
| 9352 | const timeOutId = setTimeout(() => { | ||
| 9353 | toastr.error(t`Chat deletion timed out. Please try again.`); | ||
| 9354 | setResolved(); | ||
| 9355 | }, 5000); | ||
| 9356 | |||
| 9357 | const setResolved = () => { | ||
| 9358 | if (resolved) { | ||
| 9359 | return; | ||
| 9360 | } | ||
| 9361 | resolved = true; | ||
| 9362 | [event_types.CHAT_DELETED, event_types.GROUP_CHAT_DELETED].forEach((eventType) => { | ||
| 9363 | eventSource.removeListener(eventType, setResolved); | ||
| 9364 | }); | ||
| 9365 | clearTimeout(timeOutId); | ||
| 9366 | resolve(''); | ||
| 9367 | }; | ||
| 9368 | |||
| 9369 | [event_types.CHAT_DELETED, event_types.GROUP_CHAT_DELETED].forEach((eventType) => { | ||
| 9370 | eventSource.on(eventType, setResolved); | ||
| 9371 | }); | ||
| 9372 | |||
| 9373 | const currentChatDeleteButton = $('.select_chat_block[highlight=\'true\']').parent().find('.PastChat_cross'); | ||
| 9374 | $(currentChatDeleteButton).trigger('click', { fromSlashCommand: true }); | ||
| 9375 | })); | ||
| 9376 | } | ||
| 9377 | |||
| 9378 | async function doRenameChat(_, chatName) { | ||
| 9379 | if (!chatName) { | ||
| 9380 | toastr.warning(t`Name must be provided as an argument to rename this chat.`); | ||
| 9381 | return ''; | ||
| 9382 | } | ||
| 9383 | |||
| 9384 | const currentChatName = getCurrentChatId(); | ||
| 9385 | if (!currentChatName) { | ||
| 9386 | toastr.warning(t`No chat selected that can be renamed.`); | ||
| 9387 | return ''; | ||
| 9388 | } | ||
| 9389 | |||
| 9390 | await renameChat(currentChatName, chatName); | ||
| 9391 | |||
| 9392 | toastr.success(t`Successfully renamed chat to: ${chatName}`); | ||
| 9393 | return ''; | ||
| 9394 | } | ||
| 9395 | |||
| 9396 | /** | 9101 | /** |
| 9397 | * Renames a group or character chat. | 9102 | * Renames a group or character chat. |
| 9398 | * @param {object} param Parameters for renaming chat | 9103 | * @param {object} param Parameters for renaming chat |
| @@ -9506,12 +9211,6 @@ export async function updateRemoteChatName(characterId, newName) { | |||
| 9506 | } | 9211 | } |
| 9507 | } | 9212 | } |
| 9508 | 9213 | ||
| 9509 | /** | ||
| 9510 | * /getchatname` slash command | ||
| 9511 | */ | ||
| 9512 | async function doGetChatName() { | ||
| 9513 | return getCurrentChatDetails().sessionName; | ||
| 9514 | } | ||
| 9515 | 9214 | ||
| 9516 | function doCharListDisplaySwitch() { | 9215 | function doCharListDisplaySwitch() { |
| 9517 | power_user.charListGrid = !power_user.charListGrid; | 9216 | power_user.charListGrid = !power_user.charListGrid; |
| @@ -9519,11 +9218,6 @@ function doCharListDisplaySwitch() { | |||
| 9519 | saveSettingsDebounced(); | 9218 | saveSettingsDebounced(); |
| 9520 | } | 9219 | } |
| 9521 | 9220 | ||
| 9522 | function doCloseChat() { | ||
| 9523 | $('#option_close_chat').trigger('click'); | ||
| 9524 | return ''; | ||
| 9525 | } | ||
| 9526 | |||
| 9527 | /** | 9221 | /** |
| 9528 | * Function to handle the deletion of a character, given a specific popup type and character ID. | 9222 | * Function to handle the deletion of a character, given a specific popup type and character ID. |
| 9529 | * If popup type equals "del_ch", it will proceed with deletion otherwise it will exit the function. | 9223 | * If popup type equals "del_ch", it will proceed with deletion otherwise it will exit the function. |
| @@ -9634,11 +9328,6 @@ export async function newAssistantChat({ temporary = false } = {}) { | |||
| 9634 | sendSystemMessage(system_message_types.ASSISTANT_NOTE); | 9328 | sendSystemMessage(system_message_types.ASSISTANT_NOTE); |
| 9635 | } | 9329 | } |
| 9636 | 9330 | ||
| 9637 | function doTogglePanels() { | ||
| 9638 | $('#option_settings').trigger('click'); | ||
| 9639 | return ''; | ||
| 9640 | } | ||
| 9641 | |||
| 9642 | /** | 9331 | /** |
| 9643 | * Event handler to open a navbar drawer when a drawer open button is clicked. | 9332 | * Event handler to open a navbar drawer when a drawer open button is clicked. |
| 9644 | * Handles click events on .drawer-opener elements. | 9333 | * Handles click events on .drawer-opener elements. |
| @@ -9776,249 +9465,6 @@ API Settings: ${JSON.stringify(getSettingsContents[getSettingsContents.main_api | |||
| 9776 | 9465 | ||
| 9777 | // MARK: DOM Handlers Start | 9466 | // MARK: DOM Handlers Start |
| 9778 | jQuery(async function () { | 9467 | jQuery(async function () { |
| 9779 | async function doForceSave() { | ||
| 9780 | await saveSettings(); | ||
| 9781 | await saveChatConditional(); | ||
| 9782 | toastr.success('Chat and settings saved.'); | ||
| 9783 | return ''; | ||
| 9784 | } | ||
| 9785 | |||
| 9786 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 9787 | name: 'dupe', | ||
| 9788 | callback: duplicateCharacter, | ||
| 9789 | helpString: 'Duplicates the currently selected character.', | ||
| 9790 | })); | ||
| 9791 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 9792 | name: 'api', | ||
| 9793 | callback: connectAPISlash, | ||
| 9794 | returns: 'the current API', | ||
| 9795 | namedArgumentList: [ | ||
| 9796 | SlashCommandNamedArgument.fromProps({ | ||
| 9797 | name: 'quiet', | ||
| 9798 | description: 'Suppress the toast message on connection', | ||
| 9799 | typeList: [ARGUMENT_TYPE.BOOLEAN], | ||
| 9800 | defaultValue: 'false', | ||
| 9801 | enumList: commonEnumProviders.boolean('trueFalse')(), | ||
| 9802 | }), | ||
| 9803 | ], | ||
| 9804 | unnamedArgumentList: [ | ||
| 9805 | SlashCommandArgument.fromProps({ | ||
| 9806 | description: 'API to connect to', | ||
| 9807 | typeList: [ARGUMENT_TYPE.STRING], | ||
| 9808 | enumList: Object.entries(CONNECT_API_MAP).map(([api, { selected }]) => | ||
| 9809 | new SlashCommandEnumValue(api, selected, enumTypes.getBasedOnIndex(UNIQUE_APIS.findIndex(x => x === selected)), | ||
| 9810 | selected[0].toUpperCase() ?? enumIcons.default)), | ||
| 9811 | }), | ||
| 9812 | ], | ||
| 9813 | helpString: ` | ||
| 9814 | <div> | ||
| 9815 | Connect to an API. If no argument is provided, it will return the currently connected API. | ||
| 9816 | </div> | ||
| 9817 | <div> | ||
| 9818 | <strong>Available APIs:</strong> | ||
| 9819 | <pre><code>${Object.keys(CONNECT_API_MAP).join(', ')}</code></pre> | ||
| 9820 | </div> | ||
| 9821 | `, | ||
| 9822 | })); | ||
| 9823 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 9824 | name: 'impersonate', | ||
| 9825 | callback: doImpersonate, | ||
| 9826 | aliases: ['imp'], | ||
| 9827 | namedArgumentList: [ | ||
| 9828 | new SlashCommandNamedArgument( | ||
| 9829 | 'await', | ||
| 9830 | 'Whether to await for the triggered generation before continuing', | ||
| 9831 | [ARGUMENT_TYPE.BOOLEAN], | ||
| 9832 | false, | ||
| 9833 | false, | ||
| 9834 | 'false', | ||
| 9835 | ), | ||
| 9836 | ], | ||
| 9837 | unnamedArgumentList: [ | ||
| 9838 | new SlashCommandArgument( | ||
| 9839 | 'prompt', [ARGUMENT_TYPE.STRING], false, | ||
| 9840 | ), | ||
| 9841 | ], | ||
| 9842 | helpString: ` | ||
| 9843 | <div> | ||
| 9844 | Calls an impersonation response, with an optional additional prompt. | ||
| 9845 | </div> | ||
| 9846 | <div> | ||
| 9847 | If <code>await=true</code> named argument is passed, the command will wait for the impersonation to end before continuing. | ||
| 9848 | </div> | ||
| 9849 | <div> | ||
| 9850 | <strong>Example:</strong> | ||
| 9851 | <ul> | ||
| 9852 | <li> | ||
| 9853 | <pre><code class="language-stscript">/impersonate What is the meaning of life?</code></pre> | ||
| 9854 | </li> | ||
| 9855 | </ul> | ||
| 9856 | </div> | ||
| 9857 | `, | ||
| 9858 | })); | ||
| 9859 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 9860 | name: 'delchat', | ||
| 9861 | callback: doDeleteChat, | ||
| 9862 | helpString: 'Deletes the current chat.', | ||
| 9863 | })); | ||
| 9864 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 9865 | name: 'renamechat', | ||
| 9866 | callback: doRenameChat, | ||
| 9867 | unnamedArgumentList: [ | ||
| 9868 | new SlashCommandArgument( | ||
| 9869 | 'new chat name', [ARGUMENT_TYPE.STRING], true, | ||
| 9870 | ), | ||
| 9871 | ], | ||
| 9872 | helpString: 'Renames the current chat.', | ||
| 9873 | })); | ||
| 9874 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 9875 | name: 'getchatname', | ||
| 9876 | callback: doGetChatName, | ||
| 9877 | returns: 'chat file name', | ||
| 9878 | helpString: 'Returns the name of the current chat file into the pipe.', | ||
| 9879 | })); | ||
| 9880 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 9881 | name: 'closechat', | ||
| 9882 | callback: doCloseChat, | ||
| 9883 | helpString: 'Closes the current chat.', | ||
| 9884 | })); | ||
| 9885 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 9886 | name: 'tempchat', | ||
| 9887 | callback: () => { | ||
| 9888 | return new Promise((resolve, reject) => { | ||
| 9889 | const eventCallback = async (chatId) => { | ||
| 9890 | if (chatId) { | ||
| 9891 | return reject('Not in a temporary chat'); | ||
| 9892 | } | ||
| 9893 | await newAssistantChat({ temporary: true }); | ||
| 9894 | return resolve(''); | ||
| 9895 | }; | ||
| 9896 | eventSource.once(event_types.CHAT_CHANGED, eventCallback); | ||
| 9897 | doCloseChat(); | ||
| 9898 | setTimeout(() => { | ||
| 9899 | reject('Failed to open temporary chat'); | ||
| 9900 | eventSource.removeListener(event_types.CHAT_CHANGED, eventCallback); | ||
| 9901 | }, debounce_timeout.relaxed); | ||
| 9902 | }); | ||
| 9903 | }, | ||
| 9904 | helpString: 'Opens a temporary chat with Assistant.', | ||
| 9905 | })); | ||
| 9906 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 9907 | name: 'panels', | ||
| 9908 | callback: doTogglePanels, | ||
| 9909 | aliases: ['togglepanels'], | ||
| 9910 | helpString: 'Toggle UI panels on/off', | ||
| 9911 | })); | ||
| 9912 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 9913 | name: 'forcesave', | ||
| 9914 | callback: doForceSave, | ||
| 9915 | helpString: 'Forces a save of the current chat and settings', | ||
| 9916 | })); | ||
| 9917 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 9918 | name: 'instruct', | ||
| 9919 | callback: selectInstructCallback, | ||
| 9920 | returns: 'current template', | ||
| 9921 | namedArgumentList: [ | ||
| 9922 | SlashCommandNamedArgument.fromProps({ | ||
| 9923 | name: 'quiet', | ||
| 9924 | description: 'Suppress the toast message on template change', | ||
| 9925 | typeList: [ARGUMENT_TYPE.BOOLEAN], | ||
| 9926 | defaultValue: 'false', | ||
| 9927 | enumList: commonEnumProviders.boolean('trueFalse')(), | ||
| 9928 | }), | ||
| 9929 | SlashCommandNamedArgument.fromProps({ | ||
| 9930 | name: 'forceGet', | ||
| 9931 | description: 'Force getting a name even if instruct mode is disabled', | ||
| 9932 | typeList: [ARGUMENT_TYPE.BOOLEAN], | ||
| 9933 | defaultValue: 'false', | ||
| 9934 | enumList: commonEnumProviders.boolean('trueFalse')(), | ||
| 9935 | }), | ||
| 9936 | ], | ||
| 9937 | unnamedArgumentList: [ | ||
| 9938 | SlashCommandArgument.fromProps({ | ||
| 9939 | description: 'instruct template name', | ||
| 9940 | typeList: [ARGUMENT_TYPE.STRING], | ||
| 9941 | enumProvider: () => instruct_presets.map(preset => new SlashCommandEnumValue(preset.name, null, enumTypes.enum, enumIcons.preset)), | ||
| 9942 | }), | ||
| 9943 | ], | ||
| 9944 | helpString: ` | ||
| 9945 | <div> | ||
| 9946 | Selects instruct mode template by name. Enables instruct mode if not already enabled. | ||
| 9947 | Gets the current instruct template if no name is provided and instruct mode is enabled or <code>forceGet=true</code> is passed. | ||
| 9948 | </div> | ||
| 9949 | <div> | ||
| 9950 | <strong>Example:</strong> | ||
| 9951 | <ul> | ||
| 9952 | <li> | ||
| 9953 | <pre><code class="language-stscript">/instruct creative</code></pre> | ||
| 9954 | </li> | ||
| 9955 | </ul> | ||
| 9956 | </div> | ||
| 9957 | `, | ||
| 9958 | })); | ||
| 9959 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 9960 | name: 'instruct-on', | ||
| 9961 | callback: enableInstructCallback, | ||
| 9962 | helpString: 'Enables instruct mode.', | ||
| 9963 | })); | ||
| 9964 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 9965 | name: 'instruct-off', | ||
| 9966 | callback: disableInstructCallback, | ||
| 9967 | helpString: 'Disables instruct mode', | ||
| 9968 | })); | ||
| 9969 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 9970 | name: 'instruct-state', | ||
| 9971 | aliases: ['instruct-toggle'], | ||
| 9972 | helpString: 'Gets the current instruct mode state. If an argument is provided, it will set the instruct mode state.', | ||
| 9973 | unnamedArgumentList: [ | ||
| 9974 | SlashCommandArgument.fromProps({ | ||
| 9975 | description: 'instruct mode state', | ||
| 9976 | typeList: [ARGUMENT_TYPE.BOOLEAN], | ||
| 9977 | enumList: commonEnumProviders.boolean('trueFalse')(), | ||
| 9978 | }), | ||
| 9979 | ], | ||
| 9980 | callback: async (_args, state) => { | ||
| 9981 | if (!state || typeof state !== 'string') { | ||
| 9982 | return String(power_user.instruct.enabled); | ||
| 9983 | } | ||
| 9984 | |||
| 9985 | const newState = isTrueBoolean(state); | ||
| 9986 | newState ? enableInstructCallback() : disableInstructCallback(); | ||
| 9987 | return String(power_user.instruct.enabled); | ||
| 9988 | }, | ||
| 9989 | })); | ||
| 9990 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 9991 | name: 'context', | ||
| 9992 | callback: selectContextCallback, | ||
| 9993 | returns: 'template name', | ||
| 9994 | namedArgumentList: [ | ||
| 9995 | SlashCommandNamedArgument.fromProps({ | ||
| 9996 | name: 'quiet', | ||
| 9997 | description: 'Suppress the toast message on template change', | ||
| 9998 | typeList: [ARGUMENT_TYPE.BOOLEAN], | ||
| 9999 | defaultValue: 'false', | ||
| 10000 | enumList: commonEnumProviders.boolean('trueFalse')(), | ||
| 10001 | }), | ||
| 10002 | ], | ||
| 10003 | unnamedArgumentList: [ | ||
| 10004 | SlashCommandArgument.fromProps({ | ||
| 10005 | description: 'context template name', | ||
| 10006 | typeList: [ARGUMENT_TYPE.STRING], | ||
| 10007 | enumProvider: () => context_presets.map(preset => new SlashCommandEnumValue(preset.name, null, enumTypes.enum, enumIcons.preset)), | ||
| 10008 | }), | ||
| 10009 | ], | ||
| 10010 | helpString: 'Selects context template by name. Gets the current template if no name is provided', | ||
| 10011 | })); | ||
| 10012 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 10013 | name: 'chat-manager', | ||
| 10014 | callback: () => { | ||
| 10015 | $('#option_select_chat').trigger('click'); | ||
| 10016 | return ''; | ||
| 10017 | }, | ||
| 10018 | aliases: ['chat-history', 'manage-chats'], | ||
| 10019 | helpString: 'Opens the chat manager for the current character/group.', | ||
| 10020 | })); | ||
| 10021 | |||
| 10022 | setTimeout(function () { | 9468 | setTimeout(function () { |
| 10023 | $('#groupControlsToggle').trigger('click'); | 9469 | $('#groupControlsToggle').trigger('click'); |
| 10024 | $('#groupCurrentMemberListToggle .inline-drawer-icon').trigger('click'); | 9470 | $('#groupCurrentMemberListToggle .inline-drawer-icon').trigger('click'); |
| @@ -471,7 +471,7 @@ export class ConnectionManagerRequestService { | |||
| 471 | 471 | ||
| 472 | /** | 472 | /** |
| 473 | * @param {import('./connection-manager/index.js').ConnectionProfile?} [profile] | 473 | * @param {import('./connection-manager/index.js').ConnectionProfile?} [profile] |
| 474 | * @return {import('../../script.js').ConnectAPIMap} | 474 | * @return {import('../slash-commands.js').ConnectAPIMap} |
| 475 | * @throws {Error} | 475 | * @throws {Error} |
| 476 | */ | 476 | */ |
| 477 | static validateProfile(profile) { | 477 | static validateProfile(profile) { |
| @@ -3,7 +3,6 @@ import { copyText, flashHighlight } from './utils.js'; | |||
| 3 | 3 | ||
| 4 | import { | 4 | import { |
| 5 | Generate, | 5 | Generate, |
| 6 | UNIQUE_APIS, | ||
| 7 | activateSendButtons, | 6 | activateSendButtons, |
| 8 | addOneMessage, | 7 | addOneMessage, |
| 9 | characters, | 8 | characters, |
| @@ -13,6 +12,8 @@ import { | |||
| 13 | deactivateSendButtons, | 12 | deactivateSendButtons, |
| 14 | default_avatar, | 13 | default_avatar, |
| 15 | deleteSwipe, | 14 | deleteSwipe, |
| 15 | displayPastChats, | ||
| 16 | duplicateCharacter, | ||
| 16 | eventSource, | 17 | eventSource, |
| 17 | event_types, | 18 | event_types, |
| 18 | extension_prompt_roles, | 19 | extension_prompt_roles, |
| @@ -20,6 +21,8 @@ import { | |||
| 20 | extractMessageBias, | 21 | extractMessageBias, |
| 21 | generateQuietPrompt, | 22 | generateQuietPrompt, |
| 22 | generateRaw, | 23 | generateRaw, |
| 24 | getCurrentChatDetails, | ||
| 25 | getCurrentChatId, | ||
| 23 | getFirstDisplayedMessageId, | 26 | getFirstDisplayedMessageId, |
| 24 | getThumbnailUrl, | 27 | getThumbnailUrl, |
| 25 | is_send_press, | 28 | is_send_press, |
| @@ -27,10 +30,14 @@ import { | |||
| 27 | name1, | 30 | name1, |
| 28 | name2, | 31 | name2, |
| 29 | neutralCharacterName, | 32 | neutralCharacterName, |
| 33 | newAssistantChat, | ||
| 34 | online_status, | ||
| 30 | reloadCurrentChat, | 35 | reloadCurrentChat, |
| 31 | removeMacros, | 36 | removeMacros, |
| 32 | renameCharacter, | 37 | renameCharacter, |
| 38 | renameChat, | ||
| 33 | saveChatConditional, | 39 | saveChatConditional, |
| 40 | saveSettings, | ||
| 34 | saveSettingsDebounced, | 41 | saveSettingsDebounced, |
| 35 | sendMessageAsUser, | 42 | sendMessageAsUser, |
| 36 | sendSystemMessage, | 43 | sendSystemMessage, |
| @@ -56,7 +63,7 @@ import { getRegexedString, regex_placement } from './extensions/regex/engine.js' | |||
| 56 | import { findGroupMemberId, groups, is_group_generating, openGroupById, resetSelectedGroup, saveGroupChat, selected_group, getGroupMembers } from './group-chats.js'; | 63 | import { findGroupMemberId, groups, is_group_generating, openGroupById, resetSelectedGroup, saveGroupChat, selected_group, getGroupMembers } from './group-chats.js'; |
| 57 | import { chat_completion_sources, oai_settings, promptManager } from './openai.js'; | 64 | import { chat_completion_sources, oai_settings, promptManager } from './openai.js'; |
| 58 | import { user_avatar } from './personas.js'; | 65 | import { user_avatar } from './personas.js'; |
| 59 | import { addEphemeralStoppingString, chat_styles, flushEphemeralStoppingStrings, power_user } from './power-user.js'; | 66 | import { addEphemeralStoppingString, chat_styles, context_presets, flushEphemeralStoppingStrings, power_user } from './power-user.js'; |
| 60 | import { SERVER_INPUTS, textgen_types, textgenerationwebui_settings } from './textgen-settings.js'; | 67 | import { SERVER_INPUTS, textgen_types, textgenerationwebui_settings } from './textgen-settings.js'; |
| 61 | import { decodeTextTokens, getAvailableTokenizers, getFriendlyTokenizerName, getTextTokens, getTokenCountAsync, selectTokenizer } from './tokenizers.js'; | 68 | import { decodeTextTokens, getAvailableTokenizers, getFriendlyTokenizerName, getTextTokens, getTokenCountAsync, selectTokenizer } from './tokenizers.js'; |
| 62 | import { debounce, delay, equalsIgnoreCaseAndAccents, findChar, getCharIndex, isFalseBoolean, isTrueBoolean, onlyUnique, regexFromString, showFontAwesomePicker, stringToRange, trimToEndSentence, trimToStartSentence, waitUntilCondition } from './utils.js'; | 69 | import { debounce, delay, equalsIgnoreCaseAndAccents, findChar, getCharIndex, isFalseBoolean, isTrueBoolean, onlyUnique, regexFromString, showFontAwesomePicker, stringToRange, trimToEndSentence, trimToStartSentence, waitUntilCondition } from './utils.js'; |
| @@ -80,6 +87,8 @@ import { SlashCommandDebugController } from './slash-commands/SlashCommandDebugC | |||
| 80 | import { SlashCommandScope } from './slash-commands/SlashCommandScope.js'; | 87 | import { SlashCommandScope } from './slash-commands/SlashCommandScope.js'; |
| 81 | import { t } from './i18n.js'; | 88 | import { t } from './i18n.js'; |
| 82 | import { kai_settings } from './kai-settings.js'; | 89 | import { kai_settings } from './kai-settings.js'; |
| 90 | import { instruct_presets, selectContextPreset, selectInstructPreset } from './instruct-mode.js'; | ||
| 91 | import { debounce_timeout } from './constants.js'; | ||
| 83 | export { | 92 | export { |
| 84 | executeSlashCommands, executeSlashCommandsWithOptions, getSlashCommandsHelp, registerSlashCommand, | 93 | executeSlashCommands, executeSlashCommandsWithOptions, getSlashCommandsHelp, registerSlashCommand, |
| 85 | }; | 94 | }; |
| @@ -110,7 +119,536 @@ function closureToFilter(closure) { | |||
| 110 | }; | 119 | }; |
| 111 | } | 120 | } |
| 112 | 121 | ||
| 122 | /** | ||
| 123 | * @typedef {object} ConnectAPIMap | ||
| 124 | * @property {string} selected - API name (e.g. "textgenerationwebui", "openai") | ||
| 125 | * @property {string?} [button] - CSS selector for the API button | ||
| 126 | * @property {string?} [type] - API type, mostly used by text completion. (e.g. "openrouter") | ||
| 127 | * @property {string?} [source] - API source, mostly used by chat completion. (e.g. "openai") | ||
| 128 | */ | ||
| 129 | |||
| 130 | /** @type {Record<string, ConnectAPIMap>} */ | ||
| 131 | export const CONNECT_API_MAP = {}; | ||
| 132 | |||
| 133 | /** @type {string[]} */ | ||
| 134 | export const UNIQUE_APIS = []; | ||
| 135 | |||
| 136 | function setupConnectAPIMap() { | ||
| 137 | /** @type {Record<string, ConnectAPIMap>} */ | ||
| 138 | const result = { | ||
| 139 | // Default APIs not contained inside text gen / chat gen | ||
| 140 | 'kobold': { | ||
| 141 | selected: 'kobold', | ||
| 142 | button: '#api_button', | ||
| 143 | }, | ||
| 144 | 'horde': { | ||
| 145 | selected: 'koboldhorde', | ||
| 146 | }, | ||
| 147 | 'novel': { | ||
| 148 | selected: 'novel', | ||
| 149 | button: '#api_button_novel', | ||
| 150 | }, | ||
| 151 | 'koboldcpp': { | ||
| 152 | selected: 'textgenerationwebui', | ||
| 153 | button: '#api_button_textgenerationwebui', | ||
| 154 | type: textgen_types.KOBOLDCPP, | ||
| 155 | }, | ||
| 156 | // KoboldCpp alias | ||
| 157 | 'kcpp': { | ||
| 158 | selected: 'textgenerationwebui', | ||
| 159 | button: '#api_button_textgenerationwebui', | ||
| 160 | type: textgen_types.KOBOLDCPP, | ||
| 161 | }, | ||
| 162 | 'openai': { | ||
| 163 | selected: 'openai', | ||
| 164 | button: '#api_button_openai', | ||
| 165 | source: chat_completion_sources.OPENAI, | ||
| 166 | }, | ||
| 167 | // OpenAI alias | ||
| 168 | 'oai': { | ||
| 169 | selected: 'openai', | ||
| 170 | button: '#api_button_openai', | ||
| 171 | source: chat_completion_sources.OPENAI, | ||
| 172 | }, | ||
| 173 | // Google alias | ||
| 174 | 'google': { | ||
| 175 | selected: 'openai', | ||
| 176 | button: '#api_button_openai', | ||
| 177 | source: chat_completion_sources.MAKERSUITE, | ||
| 178 | }, | ||
| 179 | // OpenRouter special naming, to differentiate between chat comp and text comp | ||
| 180 | 'openrouter': { | ||
| 181 | selected: 'openai', | ||
| 182 | button: '#api_button_openai', | ||
| 183 | source: chat_completion_sources.OPENROUTER, | ||
| 184 | }, | ||
| 185 | 'openrouter-text': { | ||
| 186 | selected: 'textgenerationwebui', | ||
| 187 | button: '#api_button_textgenerationwebui', | ||
| 188 | type: textgen_types.OPENROUTER, | ||
| 189 | }, | ||
| 190 | }; | ||
| 191 | |||
| 192 | // Fill connections map from textgen_types and chat_completion_sources | ||
| 193 | for (const textGenType of Object.values(textgen_types)) { | ||
| 194 | if (result[textGenType]) continue; | ||
| 195 | result[textGenType] = { | ||
| 196 | selected: 'textgenerationwebui', | ||
| 197 | button: '#api_button_textgenerationwebui', | ||
| 198 | type: textGenType, | ||
| 199 | }; | ||
| 200 | } | ||
| 201 | |||
| 202 | for (const chatCompletionSource of Object.values(chat_completion_sources)) { | ||
| 203 | if (result[chatCompletionSource]) continue; | ||
| 204 | result[chatCompletionSource] = { | ||
| 205 | selected: 'openai', | ||
| 206 | button: '#api_button_openai', | ||
| 207 | source: chatCompletionSource, | ||
| 208 | }; | ||
| 209 | } | ||
| 210 | |||
| 211 | Object.assign(CONNECT_API_MAP, result); | ||
| 212 | UNIQUE_APIS.push(...new Set(Object.values(CONNECT_API_MAP).map(x => x.selected))); | ||
| 213 | } | ||
| 214 | |||
| 113 | export function initDefaultSlashCommands() { | 215 | export function initDefaultSlashCommands() { |
| 216 | setupConnectAPIMap(); | ||
| 217 | |||
| 218 | async function enableInstructCallback() { | ||
| 219 | $('#instruct_enabled').prop('checked', true).trigger('input').trigger('change'); | ||
| 220 | return ''; | ||
| 221 | } | ||
| 222 | |||
| 223 | async function disableInstructCallback() { | ||
| 224 | $('#instruct_enabled').prop('checked', false).trigger('input').trigger('change'); | ||
| 225 | return ''; | ||
| 226 | } | ||
| 227 | |||
| 228 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 229 | name: 'dupe', | ||
| 230 | callback: duplicateCharacter, | ||
| 231 | helpString: 'Duplicates the currently selected character.', | ||
| 232 | })); | ||
| 233 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 234 | name: 'api', | ||
| 235 | callback: async function (args, text) { | ||
| 236 | if (!text?.toString()?.trim()) { | ||
| 237 | for (const [key, config] of Object.entries(CONNECT_API_MAP)) { | ||
| 238 | if (config.selected !== main_api) continue; | ||
| 239 | |||
| 240 | if (config.source) { | ||
| 241 | if (oai_settings.chat_completion_source === config.source) { | ||
| 242 | return key; | ||
| 243 | } else { | ||
| 244 | continue; | ||
| 245 | } | ||
| 246 | } | ||
| 247 | |||
| 248 | if (config.type) { | ||
| 249 | if (textgenerationwebui_settings.type === config.type) { | ||
| 250 | return key; | ||
| 251 | } else { | ||
| 252 | continue; | ||
| 253 | } | ||
| 254 | } | ||
| 255 | |||
| 256 | return key; | ||
| 257 | } | ||
| 258 | |||
| 259 | console.error('FIXME: The current API is not in the API map'); | ||
| 260 | return ''; | ||
| 261 | } | ||
| 262 | |||
| 263 | const apiConfig = CONNECT_API_MAP[text?.toString()?.toLowerCase() ?? '']; | ||
| 264 | if (!apiConfig) { | ||
| 265 | toastr.error(t`Error: ${text} is not a valid API`); | ||
| 266 | return ''; | ||
| 267 | } | ||
| 268 | |||
| 269 | let connectionRequired = false; | ||
| 270 | |||
| 271 | if (main_api !== apiConfig.selected) { | ||
| 272 | $(`#main_api option[value='${apiConfig.selected || text}']`).prop('selected', true); | ||
| 273 | $('#main_api').trigger('change'); | ||
| 274 | connectionRequired = true; | ||
| 275 | } | ||
| 276 | |||
| 277 | if (apiConfig.source && oai_settings.chat_completion_source !== apiConfig.source) { | ||
| 278 | $(`#chat_completion_source option[value='${apiConfig.source}']`).prop('selected', true); | ||
| 279 | $('#chat_completion_source').trigger('change'); | ||
| 280 | connectionRequired = true; | ||
| 281 | } | ||
| 282 | |||
| 283 | if (apiConfig.type && textgenerationwebui_settings.type !== apiConfig.type) { | ||
| 284 | $(`#textgen_type option[value='${apiConfig.type}']`).prop('selected', true); | ||
| 285 | $('#textgen_type').trigger('change'); | ||
| 286 | connectionRequired = true; | ||
| 287 | } | ||
| 288 | |||
| 289 | if (connectionRequired && apiConfig.button) { | ||
| 290 | $(apiConfig.button).trigger('click'); | ||
| 291 | } | ||
| 292 | |||
| 293 | const quiet = isTrueBoolean(args?.quiet?.toString()); | ||
| 294 | const toast = quiet ? jQuery() : toastr.info(t`API set to ${text}, trying to connect..`); | ||
| 295 | |||
| 296 | try { | ||
| 297 | if (connectionRequired) { | ||
| 298 | await waitUntilCondition(() => online_status !== 'no_connection', 5000, 100); | ||
| 299 | } | ||
| 300 | console.log('Connection successful'); | ||
| 301 | } catch { | ||
| 302 | console.log('Could not connect after 5 seconds, skipping.'); | ||
| 303 | } | ||
| 304 | |||
| 305 | toastr.clear(toast); | ||
| 306 | return text?.toString()?.trim() ?? ''; | ||
| 307 | }, | ||
| 308 | returns: 'the current API', | ||
| 309 | namedArgumentList: [ | ||
| 310 | SlashCommandNamedArgument.fromProps({ | ||
| 311 | name: 'quiet', | ||
| 312 | description: 'Suppress the toast message on connection', | ||
| 313 | typeList: [ARGUMENT_TYPE.BOOLEAN], | ||
| 314 | defaultValue: 'false', | ||
| 315 | enumList: commonEnumProviders.boolean('trueFalse')(), | ||
| 316 | }), | ||
| 317 | ], | ||
| 318 | unnamedArgumentList: [ | ||
| 319 | SlashCommandArgument.fromProps({ | ||
| 320 | description: 'API to connect to', | ||
| 321 | typeList: [ARGUMENT_TYPE.STRING], | ||
| 322 | enumList: Object.entries(CONNECT_API_MAP).map(([api, { selected }]) => | ||
| 323 | new SlashCommandEnumValue(api, selected, enumTypes.getBasedOnIndex(UNIQUE_APIS.findIndex(x => x === selected)), | ||
| 324 | selected[0].toUpperCase() ?? enumIcons.default)), | ||
| 325 | }), | ||
| 326 | ], | ||
| 327 | helpString: ` | ||
| 328 | <div> | ||
| 329 | Connect to an API. If no argument is provided, it will return the currently connected API. | ||
| 330 | </div> | ||
| 331 | <div> | ||
| 332 | <strong>Available APIs:</strong> | ||
| 333 | <pre><code>${Object.keys(CONNECT_API_MAP).sort((a, b) => a.localeCompare(b)).join(', ')}</code></pre> | ||
| 334 | </div> | ||
| 335 | `, | ||
| 336 | })); | ||
| 337 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 338 | name: 'impersonate', | ||
| 339 | callback: async function (args, prompt) { | ||
| 340 | const options = prompt?.toString()?.trim() ? { quiet_prompt: prompt.toString().trim(), quietToLoud: true } : {}; | ||
| 341 | const shouldAwait = isTrueBoolean(args?.await?.toString()); | ||
| 342 | const outerPromise = new Promise((outerResolve) => setTimeout(async () => { | ||
| 343 | try { | ||
| 344 | await waitUntilCondition(() => !is_send_press && !is_group_generating, 10000, 100); | ||
| 345 | } catch { | ||
| 346 | console.warn('Timeout waiting for generation unlock'); | ||
| 347 | toastr.warning(t`Cannot run /impersonate command while the reply is being generated.`); | ||
| 348 | return ''; | ||
| 349 | } | ||
| 350 | |||
| 351 | // Prevent generate recursion | ||
| 352 | $('#send_textarea').val('')[0].dispatchEvent(new Event('input', { bubbles: true })); | ||
| 353 | |||
| 354 | outerResolve(new Promise(innerResolve => setTimeout(() => innerResolve(Generate('impersonate', options)), 1))); | ||
| 355 | }, 1)); | ||
| 356 | |||
| 357 | if (shouldAwait) { | ||
| 358 | const innerPromise = await outerPromise; | ||
| 359 | await innerPromise; | ||
| 360 | } | ||
| 361 | |||
| 362 | return ''; | ||
| 363 | } | ||
| 364 | , | ||
| 365 | aliases: ['imp'], | ||
| 366 | namedArgumentList: [ | ||
| 367 | new SlashCommandNamedArgument( | ||
| 368 | 'await', | ||
| 369 | 'Whether to await for the triggered generation before continuing', | ||
| 370 | [ARGUMENT_TYPE.BOOLEAN], | ||
| 371 | false, | ||
| 372 | false, | ||
| 373 | 'false', | ||
| 374 | ), | ||
| 375 | ], | ||
| 376 | unnamedArgumentList: [ | ||
| 377 | new SlashCommandArgument( | ||
| 378 | 'prompt', [ARGUMENT_TYPE.STRING], false, | ||
| 379 | ), | ||
| 380 | ], | ||
| 381 | helpString: ` | ||
| 382 | <div> | ||
| 383 | Calls an impersonation response, with an optional additional prompt. | ||
| 384 | </div> | ||
| 385 | <div> | ||
| 386 | If <code>await=true</code> named argument is passed, the command will wait for the impersonation to end before continuing. | ||
| 387 | </div> | ||
| 388 | <div> | ||
| 389 | <strong>Example:</strong> | ||
| 390 | <ul> | ||
| 391 | <li> | ||
| 392 | <pre><code class="language-stscript">/impersonate What is the meaning of life?</code></pre> | ||
| 393 | </li> | ||
| 394 | </ul> | ||
| 395 | </div> | ||
| 396 | `, | ||
| 397 | })); | ||
| 398 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 399 | name: 'delchat', | ||
| 400 | callback: async function () { | ||
| 401 | return displayPastChats().then(() => new Promise((resolve) => { | ||
| 402 | let resolved = false; | ||
| 403 | const timeOutId = setTimeout(() => { | ||
| 404 | toastr.error(t`Chat deletion timed out. Please try again.`); | ||
| 405 | setResolved(); | ||
| 406 | }, 5000); | ||
| 407 | |||
| 408 | const setResolved = () => { | ||
| 409 | if (resolved) { | ||
| 410 | return; | ||
| 411 | } | ||
| 412 | resolved = true; | ||
| 413 | [event_types.CHAT_DELETED, event_types.GROUP_CHAT_DELETED].forEach((eventType) => { | ||
| 414 | eventSource.removeListener(eventType, setResolved); | ||
| 415 | }); | ||
| 416 | clearTimeout(timeOutId); | ||
| 417 | resolve(''); | ||
| 418 | }; | ||
| 419 | |||
| 420 | [event_types.CHAT_DELETED, event_types.GROUP_CHAT_DELETED].forEach((eventType) => { | ||
| 421 | eventSource.on(eventType, setResolved); | ||
| 422 | }); | ||
| 423 | |||
| 424 | const currentChatDeleteButton = $('.select_chat_block[highlight=\'true\']').parent().find('.PastChat_cross'); | ||
| 425 | $(currentChatDeleteButton).trigger('click', { fromSlashCommand: true }); | ||
| 426 | })); | ||
| 427 | }, | ||
| 428 | helpString: 'Deletes the current chat.', | ||
| 429 | })); | ||
| 430 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 431 | name: 'renamechat', | ||
| 432 | callback: async function doRenameChat(_, chatName) { | ||
| 433 | if (!chatName) { | ||
| 434 | toastr.warning(t`Name must be provided as an argument to rename this chat.`); | ||
| 435 | return ''; | ||
| 436 | } | ||
| 437 | |||
| 438 | const currentChatName = getCurrentChatId(); | ||
| 439 | if (!currentChatName) { | ||
| 440 | toastr.warning(t`No chat selected that can be renamed.`); | ||
| 441 | return ''; | ||
| 442 | } | ||
| 443 | |||
| 444 | await renameChat(currentChatName, chatName.toString()); | ||
| 445 | |||
| 446 | toastr.success(t`Successfully renamed chat to: ${chatName}`); | ||
| 447 | return ''; | ||
| 448 | }, | ||
| 449 | unnamedArgumentList: [ | ||
| 450 | new SlashCommandArgument( | ||
| 451 | 'new chat name', [ARGUMENT_TYPE.STRING], true, | ||
| 452 | ), | ||
| 453 | ], | ||
| 454 | helpString: 'Renames the current chat.', | ||
| 455 | })); | ||
| 456 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 457 | name: 'getchatname', | ||
| 458 | callback: async function doGetChatName() { | ||
| 459 | return getCurrentChatDetails().sessionName; | ||
| 460 | }, | ||
| 461 | returns: 'chat file name', | ||
| 462 | helpString: 'Returns the name of the current chat file into the pipe.', | ||
| 463 | })); | ||
| 464 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 465 | name: 'closechat', | ||
| 466 | callback: function () { | ||
| 467 | $('#option_close_chat').trigger('click'); | ||
| 468 | return ''; | ||
| 469 | }, | ||
| 470 | helpString: 'Closes the current chat.', | ||
| 471 | })); | ||
| 472 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 473 | name: 'tempchat', | ||
| 474 | callback: () => { | ||
| 475 | return new Promise((resolve, reject) => { | ||
| 476 | const eventCallback = async (chatId) => { | ||
| 477 | if (chatId) { | ||
| 478 | return reject('Not in a temporary chat'); | ||
| 479 | } | ||
| 480 | await newAssistantChat({ temporary: true }); | ||
| 481 | return resolve(''); | ||
| 482 | }; | ||
| 483 | eventSource.once(event_types.CHAT_CHANGED, eventCallback); | ||
| 484 | $('#option_close_chat').trigger('click'); | ||
| 485 | setTimeout(() => { | ||
| 486 | reject('Failed to open temporary chat'); | ||
| 487 | eventSource.removeListener(event_types.CHAT_CHANGED, eventCallback); | ||
| 488 | }, debounce_timeout.relaxed); | ||
| 489 | }); | ||
| 490 | }, | ||
| 491 | helpString: 'Opens a temporary chat with Assistant.', | ||
| 492 | })); | ||
| 493 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 494 | name: 'panels', | ||
| 495 | callback: function () { | ||
| 496 | $('#option_settings').trigger('click'); | ||
| 497 | return ''; | ||
| 498 | }, | ||
| 499 | aliases: ['togglepanels'], | ||
| 500 | helpString: 'Toggle UI panels on/off', | ||
| 501 | })); | ||
| 502 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 503 | name: 'forcesave', | ||
| 504 | callback: async function () { | ||
| 505 | await saveSettings(); | ||
| 506 | await saveChatConditional(); | ||
| 507 | toastr.success('Chat and settings saved.'); | ||
| 508 | return ''; | ||
| 509 | }, | ||
| 510 | helpString: 'Forces a save of the current chat and settings', | ||
| 511 | })); | ||
| 512 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 513 | name: 'instruct', | ||
| 514 | callback: async function (args, name) { | ||
| 515 | if (!name) { | ||
| 516 | return power_user.instruct.enabled || isTrueBoolean(args?.forceGet?.toString()) ? power_user.instruct.preset : ''; | ||
| 517 | } | ||
| 518 | |||
| 519 | const quiet = isTrueBoolean(args?.quiet?.toString()); | ||
| 520 | const instructNames = instruct_presets.map(preset => preset.name); | ||
| 521 | const fuse = new Fuse(instructNames); | ||
| 522 | const result = fuse.search(name?.toString() ?? ''); | ||
| 523 | |||
| 524 | if (result.length === 0) { | ||
| 525 | !quiet && toastr.warning(t`Instruct template '${name}' not found`); | ||
| 526 | return ''; | ||
| 527 | } | ||
| 528 | |||
| 529 | const foundName = result[0].item; | ||
| 530 | selectInstructPreset(foundName, { quiet: quiet }); | ||
| 531 | return foundName; | ||
| 532 | }, | ||
| 533 | returns: 'current template', | ||
| 534 | namedArgumentList: [ | ||
| 535 | SlashCommandNamedArgument.fromProps({ | ||
| 536 | name: 'quiet', | ||
| 537 | description: 'Suppress the toast message on template change', | ||
| 538 | typeList: [ARGUMENT_TYPE.BOOLEAN], | ||
| 539 | defaultValue: 'false', | ||
| 540 | enumList: commonEnumProviders.boolean('trueFalse')(), | ||
| 541 | }), | ||
| 542 | SlashCommandNamedArgument.fromProps({ | ||
| 543 | name: 'forceGet', | ||
| 544 | description: 'Force getting a name even if instruct mode is disabled', | ||
| 545 | typeList: [ARGUMENT_TYPE.BOOLEAN], | ||
| 546 | defaultValue: 'false', | ||
| 547 | enumList: commonEnumProviders.boolean('trueFalse')(), | ||
| 548 | }), | ||
| 549 | ], | ||
| 550 | unnamedArgumentList: [ | ||
| 551 | SlashCommandArgument.fromProps({ | ||
| 552 | description: 'instruct template name', | ||
| 553 | typeList: [ARGUMENT_TYPE.STRING], | ||
| 554 | enumProvider: () => instruct_presets.map(preset => new SlashCommandEnumValue(preset.name, null, enumTypes.enum, enumIcons.preset)), | ||
| 555 | }), | ||
| 556 | ], | ||
| 557 | helpString: ` | ||
| 558 | <div> | ||
| 559 | Selects instruct mode template by name. Enables instruct mode if not already enabled. | ||
| 560 | Gets the current instruct template if no name is provided and instruct mode is enabled or <code>forceGet=true</code> is passed. | ||
| 561 | </div> | ||
| 562 | <div> | ||
| 563 | <strong>Example:</strong> | ||
| 564 | <ul> | ||
| 565 | <li> | ||
| 566 | <pre><code class="language-stscript">/instruct creative</code></pre> | ||
| 567 | </li> | ||
| 568 | </ul> | ||
| 569 | </div> | ||
| 570 | `, | ||
| 571 | })); | ||
| 572 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 573 | name: 'instruct-on', | ||
| 574 | callback: enableInstructCallback, | ||
| 575 | helpString: 'Enables instruct mode.', | ||
| 576 | })); | ||
| 577 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 578 | name: 'instruct-off', | ||
| 579 | callback: disableInstructCallback, | ||
| 580 | helpString: 'Disables instruct mode', | ||
| 581 | })); | ||
| 582 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 583 | name: 'instruct-state', | ||
| 584 | aliases: ['instruct-toggle'], | ||
| 585 | helpString: 'Gets the current instruct mode state. If an argument is provided, it will set the instruct mode state.', | ||
| 586 | unnamedArgumentList: [ | ||
| 587 | SlashCommandArgument.fromProps({ | ||
| 588 | description: 'instruct mode state', | ||
| 589 | typeList: [ARGUMENT_TYPE.BOOLEAN], | ||
| 590 | enumList: commonEnumProviders.boolean('trueFalse')(), | ||
| 591 | }), | ||
| 592 | ], | ||
| 593 | callback: async (_args, state) => { | ||
| 594 | if (!state || typeof state !== 'string') { | ||
| 595 | return String(power_user.instruct.enabled); | ||
| 596 | } | ||
| 597 | |||
| 598 | const newState = isTrueBoolean(state); | ||
| 599 | newState ? enableInstructCallback() : disableInstructCallback(); | ||
| 600 | return String(power_user.instruct.enabled); | ||
| 601 | }, | ||
| 602 | })); | ||
| 603 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 604 | name: 'context', | ||
| 605 | callback: async function (args, name) { | ||
| 606 | if (!name) { | ||
| 607 | return power_user.context.preset; | ||
| 608 | } | ||
| 609 | |||
| 610 | const quiet = isTrueBoolean(args?.quiet?.toString()); | ||
| 611 | const contextNames = context_presets.map(preset => preset.name); | ||
| 612 | const fuse = new Fuse(contextNames); | ||
| 613 | const result = fuse.search(name?.toString() ?? ''); | ||
| 614 | |||
| 615 | if (result.length === 0) { | ||
| 616 | !quiet && toastr.warning(t`Context template '${name}' not found`); | ||
| 617 | return ''; | ||
| 618 | } | ||
| 619 | |||
| 620 | const foundName = result[0].item; | ||
| 621 | selectContextPreset(foundName, { quiet: quiet }); | ||
| 622 | return foundName; | ||
| 623 | }, | ||
| 624 | returns: 'template name', | ||
| 625 | namedArgumentList: [ | ||
| 626 | SlashCommandNamedArgument.fromProps({ | ||
| 627 | name: 'quiet', | ||
| 628 | description: 'Suppress the toast message on template change', | ||
| 629 | typeList: [ARGUMENT_TYPE.BOOLEAN], | ||
| 630 | defaultValue: 'false', | ||
| 631 | enumList: commonEnumProviders.boolean('trueFalse')(), | ||
| 632 | }), | ||
| 633 | ], | ||
| 634 | unnamedArgumentList: [ | ||
| 635 | SlashCommandArgument.fromProps({ | ||
| 636 | description: 'context template name', | ||
| 637 | typeList: [ARGUMENT_TYPE.STRING], | ||
| 638 | enumProvider: () => context_presets.map(preset => new SlashCommandEnumValue(preset.name, null, enumTypes.enum, enumIcons.preset)), | ||
| 639 | }), | ||
| 640 | ], | ||
| 641 | helpString: 'Selects context template by name. Gets the current template if no name is provided', | ||
| 642 | })); | ||
| 643 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 644 | name: 'chat-manager', | ||
| 645 | callback: () => { | ||
| 646 | $('#option_select_chat').trigger('click'); | ||
| 647 | return ''; | ||
| 648 | }, | ||
| 649 | aliases: ['chat-history', 'manage-chats'], | ||
| 650 | helpString: 'Opens the chat manager for the current character/group.', | ||
| 651 | })); | ||
| 114 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | 652 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 115 | name: '?', | 653 | name: '?', |
| 116 | callback: helpCommandCallback, | 654 | callback: helpCommandCallback, |
| @@ -3558,7 +4096,7 @@ async function countGroupMemberCallback() { | |||
| 3558 | return ''; | 4096 | return ''; |
| 3559 | } | 4097 | } |
| 3560 | 4098 | ||
| 3561 | return getGroupMembers(selected_group).length; | 4099 | return String(getGroupMembers(selected_group).length); |
| 3562 | } | 4100 | } |
| 3563 | 4101 | ||
| 3564 | async function removeGroupMemberCallback(_, arg) { | 4102 | async function removeGroupMemberCallback(_, arg) { |
| @@ -4684,18 +5222,21 @@ export function stopScriptExecution() { | |||
| 4684 | */ | 5222 | */ |
| 4685 | async function clearCommandProgress() { | 5223 | async function clearCommandProgress() { |
| 4686 | if (isExecutingCommandsFromChatInput) return; | 5224 | if (isExecutingCommandsFromChatInput) return; |
| 4687 | document.querySelector('#send_textarea').style.setProperty('--progDone', '1'); | 5225 | const ta = document.getElementById('send_textarea'); |
| 5226 | const fs = document.getElementById('form_sheld'); | ||
| 5227 | if (!ta || !fs) return; | ||
| 5228 | ta.style.setProperty('--progDone', '1'); | ||
| 4688 | await delay(250); | 5229 | await delay(250); |
| 4689 | if (isExecutingCommandsFromChatInput) return; | 5230 | if (isExecutingCommandsFromChatInput) return; |
| 4690 | document.querySelector('#send_textarea').style.transition = 'none'; | 5231 | ta.style.transition = 'none'; |
| 4691 | await delay(1); | 5232 | await delay(1); |
| 4692 | document.querySelector('#send_textarea').style.setProperty('--prog', '0%'); | 5233 | ta.style.setProperty('--prog', '0%'); |
| 4693 | document.querySelector('#send_textarea').style.setProperty('--progDone', '0'); | 5234 | ta.style.setProperty('--progDone', '0'); |
| 4694 | document.querySelector('#form_sheld').classList.remove('script_success'); | 5235 | fs.classList.remove('script_success'); |
| 4695 | document.querySelector('#form_sheld').classList.remove('script_error'); | 5236 | fs.classList.remove('script_error'); |
| 4696 | document.querySelector('#form_sheld').classList.remove('script_aborted'); | 5237 | fs.classList.remove('script_aborted'); |
| 4697 | await delay(1); | 5238 | await delay(1); |
| 4698 | document.querySelector('#send_textarea').style.transition = null; | 5239 | ta.style.transition = null; |
| 4699 | } | 5240 | } |
| 4700 | /** | 5241 | /** |
| 4701 | * Debounced version of clearCommandProgress. | 5242 | * Debounced version of clearCommandProgress. |
| @@ -4742,19 +5283,20 @@ export async function executeSlashCommandsOnChatInput(text, options = {}) { | |||
| 4742 | commandsFromChatInputAbortController?.abort('processCommands was called'); | 5283 | commandsFromChatInputAbortController?.abort('processCommands was called'); |
| 4743 | activateScriptButtons(); | 5284 | activateScriptButtons(); |
| 4744 | 5285 | ||
| 4745 | /**@type {HTMLTextAreaElement}*/ | 5286 | /** @type {HTMLTextAreaElement} */ |
| 4746 | const ta = document.querySelector('#send_textarea'); | 5287 | const ta = document.querySelector('#send_textarea'); |
| 5288 | const fs = document.querySelector('#form_sheld'); | ||
| 4747 | 5289 | ||
| 4748 | if (options.clearChatInput) { | 5290 | if (options.clearChatInput) { |
| 4749 | ta.value = ''; | 5291 | ta.value = ''; |
| 4750 | ta.dispatchEvent(new Event('input', { bubbles: true })); | 5292 | ta.dispatchEvent(new Event('input', { bubbles: true })); |
| 4751 | } | 5293 | } |
| 4752 | 5294 | ||
| 4753 | document.querySelector('#send_textarea').style.setProperty('--prog', '0%'); | 5295 | ta.style.setProperty('--prog', '0%'); |
| 4754 | document.querySelector('#send_textarea').style.setProperty('--progDone', '0'); | 5296 | ta.style.setProperty('--progDone', '0'); |
| 4755 | document.querySelector('#form_sheld').classList.remove('script_success'); | 5297 | fs.classList.remove('script_success'); |
| 4756 | document.querySelector('#form_sheld').classList.remove('script_error'); | 5298 | fs.classList.remove('script_error'); |
| 4757 | document.querySelector('#form_sheld').classList.remove('script_aborted'); | 5299 | fs.classList.remove('script_aborted'); |
| 4758 | 5300 | ||
| 4759 | /**@type {SlashCommandClosureResult} */ | 5301 | /**@type {SlashCommandClosureResult} */ |
| 4760 | let result = null; | 5302 | let result = null; |
| @@ -4905,7 +5447,7 @@ async function executeSlashCommandsWithOptions(text, options = {}) { | |||
| 4905 | * @param {boolean} handleParserErrors Whether to handle parser errors (show toast on error) or throw | 5447 | * @param {boolean} handleParserErrors Whether to handle parser errors (show toast on error) or throw |
| 4906 | * @param {SlashCommandScope} scope The scope to be used when executing the commands. | 5448 | * @param {SlashCommandScope} scope The scope to be used when executing the commands. |
| 4907 | * @param {boolean} handleExecutionErrors Whether to handle execution errors (show toast on error) or throw | 5449 | * @param {boolean} handleExecutionErrors Whether to handle execution errors (show toast on error) or throw |
| 4908 | * @param {{[id:PARSER_FLAG]:boolean}} parserFlags Parser flags to apply | 5450 | * @param {{[id:import('./slash-commands/SlashCommandParser.js').PARSER_FLAG]:boolean}} parserFlags Parser flags to apply |
| 4909 | * @param {SlashCommandAbortController} abortController Controller used to abort or pause command execution | 5451 | * @param {SlashCommandAbortController} abortController Controller used to abort or pause command execution |
| 4910 | * @param {(done:number, total:number)=>void} onProgress Callback to handle progress events | 5452 | * @param {(done:number, total:number)=>void} onProgress Callback to handle progress events |
| 4911 | * @returns {Promise<SlashCommandClosureResult>} | 5453 | * @returns {Promise<SlashCommandClosureResult>} |