Merge branch 'staging' into macro-register

This commit is contained in:
Cohee 2024-06-26 21:54:00 +03:00
commit ef0772bc9f
23 changed files with 284 additions and 170 deletions

View File

@ -36,9 +36,6 @@ label[for="extensions_autoconnect"] {
.extensions_info {
text-align: left;
max-height: 100%;
overflow-y: auto;
padding-right: 1em;
}
.extensions_info h3 {

View File

@ -115,12 +115,10 @@ dialog {
background-color: var(--crimson-hover);
}
.menu_button.popup-button-custom {
/* Custom buttons should not scale to smallest size, otherwise they will always break to multiline */
width: unset;
}
.popup-controls .menu_button {
/* Popup buttons should not scale to smallest size, otherwise they will always break to multiline if multiple words */
width: unset;
/* Fix weird animation issue with fonts on brightness filter */
backface-visibility: hidden;
transform: translateZ(0);

6
public/img/manual.svg Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<svg width="800px" height="800px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path d="M13.3252 3.05011L8.66765 20.4323L10.5995 20.9499L15.257 3.56775L13.3252 3.05011Z" />
<path d="M7.61222 18.3608L8.97161 16.9124L8.9711 16.8933L3.87681 12.1121L8.66724 7.00798L7.20892 5.63928L1.0498 12.2017L7.61222 18.3608Z" />
<path d="M16.3883 18.3608L15.0289 16.9124L15.0294 16.8933L20.1237 12.1121L15.3333 7.00798L16.7916 5.63928L22.9507 12.2017L16.3883 18.3608Z" />
</svg>

After

Width:  |  Height:  |  Size: 514 B

View File

@ -6400,9 +6400,6 @@
</div>
</div>
</template>
<div id="rawPromptPopup" class="list-group">
<div id="rawPromptWrapper" class="tokenItemizingSubclass"></div>
</div>
<div id="user_avatar_template" class="template_element">
<div class="avatar-container">
<div imgfile="" class="avatar">

View File

@ -228,7 +228,7 @@ import { appendFileContent, hasPendingFileAttachment, populateFileAttachment, de
import { initPresetManager } from './scripts/preset-manager.js';
import { MacrosParser, evaluateMacros } from './scripts/macros.js';
import { currentUser, setUserControls } from './scripts/user.js';
import { POPUP_TYPE, Popup, callGenericPopup, fixToastrForDialogs } from './scripts/popup.js';
import { POPUP_RESULT, POPUP_TYPE, Popup, callGenericPopup, fixToastrForDialogs } from './scripts/popup.js';
import { renderTemplate, renderTemplateAsync } from './scripts/templates.js';
import { ScraperManager } from './scripts/scrapers.js';
import { SlashCommandParser } from './scripts/slash-commands/SlashCommandParser.js';
@ -513,9 +513,6 @@ let optionsPopper = Popper.createPopper(document.getElementById('options_button'
let exportPopper = Popper.createPopper(document.getElementById('export_button'), document.getElementById('export_format_popup'), {
placement: 'left',
});
let rawPromptPopper = Popper.createPopper(document.getElementById('dialogue_popup'), document.getElementById('rawPromptPopup'), {
placement: 'right',
});
// Saved here for performance reasons
const messageTemplate = $('#message_template .mes');
@ -4890,14 +4887,44 @@ async function promptItemize(itemizedPrompts, requestedMesId) {
const params = await itemizedParams(itemizedPrompts, thisPromptSet);
if (params.this_main_api == 'openai') {
const template = await renderTemplateAsync('itemizationChat', params);
callPopup(template, 'text');
const template = params.this_main_api == 'openai'
? await renderTemplateAsync('itemizationChat', params)
: await renderTemplateAsync('itemizationText', params);
} else {
const template = await renderTemplateAsync('itemizationText', params);
callPopup(template, 'text');
}
const popup = new Popup(template, POPUP_TYPE.TEXT);
popup.dlg.querySelector('#copyPromptToClipboard').addEventListener('click', function () {
let rawPrompt = itemizedPrompts[PromptArrayItemForRawPromptDisplay].rawPrompt;
let rawPromptValues = rawPrompt;
if (Array.isArray(rawPrompt)) {
rawPromptValues = rawPrompt.map(x => x.content).join('\n');
}
navigator.clipboard.writeText(rawPromptValues);
toastr.info('Copied!');
});
popup.dlg.querySelector('#showRawPrompt').addEventListener('click', function () {
//console.log(itemizedPrompts[PromptArrayItemForRawPromptDisplay].rawPrompt);
console.log(PromptArrayItemForRawPromptDisplay);
console.log(itemizedPrompts);
console.log(itemizedPrompts[PromptArrayItemForRawPromptDisplay].rawPrompt);
let rawPrompt = itemizedPrompts[PromptArrayItemForRawPromptDisplay].rawPrompt;
let rawPromptValues = rawPrompt;
if (Array.isArray(rawPrompt)) {
rawPromptValues = rawPrompt.map(x => x.content).join('\n');
}
//let DisplayStringifiedPrompt = JSON.stringify(itemizedPrompts[PromptArrayItemForRawPromptDisplay].rawPrompt).replace(/\n+/g, '<br>');
const rawPromptWrapper = document.getElementById('rawPromptWrapper');
rawPromptWrapper.innerText = rawPromptValues;
$('#rawPromptPopup').slideToggle();
});
await popup.show();
}
function setInContextMessages(lastmsg, type) {
@ -9184,13 +9211,6 @@ jQuery(async function () {
if (popup_type == 'alternate_greeting' && menu_type !== 'create') {
createOrEditCharacter();
}
if (popup_type === 'del_group') {
const groupId = $('#dialogue_popup').data('group_id');
if (groupId) {
deleteGroup(groupId);
}
}
//Make a new chat for selected character
if (
popup_type == 'new_chat' &&
@ -9224,9 +9244,6 @@ jQuery(async function () {
}
}
rawPromptPopper.update();
$('#rawPromptPopup').hide();
if (dialogueResolve) {
if (popup_type == 'input') {
dialogueResolve($('#dialogue_popup_input').val());
@ -9817,45 +9834,14 @@ jQuery(async function () {
});
}
$(document).on('pointerup', '.mes_prompt', function () {
$(document).on('pointerup', '.mes_prompt', async function () {
let mesIdForItemization = $(this).closest('.mes').attr('mesId');
console.log(`looking for mesID: ${mesIdForItemization}`);
if (itemizedPrompts.length !== undefined && itemizedPrompts.length !== 0) {
promptItemize(itemizedPrompts, mesIdForItemization);
await promptItemize(itemizedPrompts, mesIdForItemization);
}
});
$(document).on('pointerup', '#copyPromptToClipboard', function () {
let rawPrompt = itemizedPrompts[PromptArrayItemForRawPromptDisplay].rawPrompt;
let rawPromptValues = rawPrompt;
if (Array.isArray(rawPrompt)) {
rawPromptValues = rawPrompt.map(x => x.content).join('\n');
}
navigator.clipboard.writeText(rawPromptValues);
toastr.info('Copied!', '', { timeOut: 2000 });
});
$(document).on('pointerup', '#showRawPrompt', function () {
//console.log(itemizedPrompts[PromptArrayItemForRawPromptDisplay].rawPrompt);
console.log(PromptArrayItemForRawPromptDisplay);
console.log(itemizedPrompts);
console.log(itemizedPrompts[PromptArrayItemForRawPromptDisplay].rawPrompt);
let rawPrompt = itemizedPrompts[PromptArrayItemForRawPromptDisplay].rawPrompt;
let rawPromptValues = rawPrompt;
if (Array.isArray(rawPrompt)) {
rawPromptValues = rawPrompt.map(x => x.content).join('\n');
}
//let DisplayStringifiedPrompt = JSON.stringify(itemizedPrompts[PromptArrayItemForRawPromptDisplay].rawPrompt).replace(/\n+/g, '<br>');
$('#rawPromptWrapper').text(rawPromptValues);
rawPromptPopper.update();
$('#rawPromptPopup').toggle();
});
//********************
//***Message Editor***
$(document).on('click', '.mes_edit', async function () {
@ -10060,7 +10046,7 @@ jQuery(async function () {
});
$(document).on('click', '.mes_edit_copy', async function () {
const confirmation = await callPopup('Create a copy of this message?', 'confirm');
const confirmation = await callGenericPopup('Create a copy of this message?', POPUP_TYPE.CONFIRM);
if (!confirmation) {
return;
}
@ -10086,24 +10072,33 @@ jQuery(async function () {
$(document).on('click', '.mes_edit_delete', async function (event, customData) {
const fromSlashCommand = customData?.fromSlashCommand || false;
const swipeExists = (!Array.isArray(chat[this_edit_mes_id].swipes) || chat[this_edit_mes_id].swipes.length <= 1 || chat[this_edit_mes_id].is_user || parseInt(this_edit_mes_id) !== chat.length - 1);
const canDeleteSwipe = (Array.isArray(chat[this_edit_mes_id].swipes) && chat[this_edit_mes_id].swipes.length > 1 && !chat[this_edit_mes_id].is_user && parseInt(this_edit_mes_id) === chat.length - 1);
let deleteOnlySwipe = false;
if (power_user.confirm_message_delete && fromSlashCommand !== true) {
const confirmation = swipeExists ? await callPopup('Are you sure you want to delete this message?', 'confirm')
: await callPopup('<h3>Delete this...</h3> <select id=\'del_type\'><option value=\'swipe\'>Swipe</option><option value=\'message\'>Message</option></select>', 'confirm');
if (!confirmation) {
const result = await callGenericPopup('Are you sure you want to delete this message?', POPUP_TYPE.CONFIRM, null, {
okButton: canDeleteSwipe ? 'Delete Swipe' : 'Delete Message',
cancelButton: 'Cancel',
customButtons: canDeleteSwipe ? ['Delete Message'] : null,
});
if (!result) {
return;
}
deleteOnlySwipe = canDeleteSwipe && result === 1; // Default button, not the custom one
}
const mes = $(this).closest('.mes');
if (!mes) {
const messageElement = $(this).closest('.mes');
if (!messageElement) {
return;
}
if ($('#del_type').val() === 'swipe') {
const swipe_id = chat[this_edit_mes_id]['swipe_id'];
chat[this_edit_mes_id]['swipes'].splice(swipe_id, 1);
if (deleteOnlySwipe) {
const message = chat[this_edit_mes_id];
const swipe_id = message.swipe_id;
message.swipes.splice(swipe_id, 1);
if (Array.isArray(message.swipe_info) && message.swipe_info.length) {
message.swipe_info.splice(swipe_id, 1);
}
if (swipe_id > 0) {
$('.swipe_left:last').click();
} else {
@ -10111,7 +10106,7 @@ jQuery(async function () {
}
} else {
chat.splice(this_edit_mes_id, 1);
mes.remove();
messageElement.remove();
}
let startFromZero = Number(this_edit_mes_id) === 0;

View File

@ -16,7 +16,6 @@ import {
eventSource,
menu_type,
substituteParams,
callPopup,
sendTextareaMessage,
} from '../script.js';
@ -1004,20 +1003,21 @@ export function initRossMods() {
if (skipConfirm) {
doRegenerate();
} else {
const popupText = `
<div class="marginBot10">Are you sure you want to regenerate the latest message?</div>
<label class="checkbox_label justifyCenter" for="regenerateWithCtrlEnter">
<input type="checkbox" id="regenerateWithCtrlEnter">
Don't ask again
</label>`;
callPopup(popupText, 'confirm').then(result => {
if (!result) {
return;
}
const regenerateWithCtrlEnter = $('#regenerateWithCtrlEnter').prop('checked');
SaveLocal(skipConfirmKey, regenerateWithCtrlEnter);
doRegenerate();
});
Popup.show.confirm('Regenerate Message', `
<span>Are you sure you want to regenerate the latest message?</span>
<label class="checkbox_label justifyCenter marginTop10" for="regenerateWithCtrlEnter">
<input type="checkbox" id="regenerateWithCtrlEnter">
Don't ask again
</label>`, {
onClose: (popup) => {
if (!popup.result) {
return;
}
const regenerateWithCtrlEnter = $('#regenerateWithCtrlEnter').prop('checked');
SaveLocal(skipConfirmKey, regenerateWithCtrlEnter);
doRegenerate();
},
})
}
return;
} else {

View File

@ -504,7 +504,7 @@ function addExtensionScript(name, manifest) {
* @param {boolean} isDisabled - Whether the extension is disabled or not.
* @param {boolean} isExternal - Whether the extension is external or not.
* @param {string} checkboxClass - The class for the checkbox HTML element.
* @return {string} - The HTML string that represents the extension.
* @return {Promise<string>} - The HTML string that represents the extension.
*/
async function generateExtensionHtml(name, manifest, isActive, isDisabled, isExternal, checkboxClass) {
const displayName = manifest.display_name;
@ -556,8 +556,10 @@ async function generateExtensionHtml(name, manifest, isActive, isDisabled, isExt
} else if (!isDisabled) { // Neither active nor disabled
const requirements = new Set(manifest.requires);
modules.forEach(x => requirements.delete(x));
const requirementsString = DOMPurify.sanitize([...requirements].join(', '));
extensionHtml += `<p>Missing modules: <span class="failure">${requirementsString}</span></p>`;
if (requirements.size > 0) {
const requirementsString = DOMPurify.sanitize([...requirements].join(', '));
extensionHtml += `<p>Missing modules: <span class="failure">${requirementsString}</span></p>`;
}
}
return extensionHtml;
@ -642,7 +644,7 @@ async function showExtensionsDetails() {
popup.complete(POPUP_RESULT.AFFIRMATIVE);
},
};
const popup = new Popup(`<div class="extensions_info">${html}</div>`, POPUP_TYPE.TEXT, '', { okButton: 'Close', wide: true, large: true, customButtons: [updateAllButton] });
const popup = new Popup(`<div class="extensions_info">${html}</div>`, POPUP_TYPE.TEXT, '', { okButton: 'Close', wide: true, large: true, customButtons: [updateAllButton], allowVerticalScrolling: true });
popupPromise = popup.show();
} catch (error) {
toastr.error('Error loading extensions. See browser console for details.');

View File

@ -1,10 +1,11 @@
import { callPopup, main_api } from '../../../script.js';
import { main_api } from '../../../script.js';
import { getContext } from '../../extensions.js';
import { SlashCommand } from '../../slash-commands/SlashCommand.js';
import { SlashCommandParser } from '../../slash-commands/SlashCommandParser.js';
import { getFriendlyTokenizerName, getTextTokens, getTokenCountAsync, tokenizers } from '../../tokenizers.js';
import { resetScrollHeight, debounce } from '../../utils.js';
import { debounce_timeout } from '../../constants.js';
import { POPUP_TYPE, callGenericPopup } from '../../popup.js';
function rgb2hex(rgb) {
rgb = rgb.match(/^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?/i);
@ -63,8 +64,7 @@ async function doTokenCounter() {
}, debounce_timeout.relaxed);
dialog.find('#token_counter_textarea').on('input', () => countDebounced());
$('#dialogue_popup').addClass('wide_dialogue_popup');
callPopup(dialog, 'text', '', { wide: true, large: true });
callGenericPopup(dialog, POPUP_TYPE.TEXT, '', { wide: true, large: true, allowVerticalScrolling: true });
}
/**

View File

@ -1,7 +1,6 @@
export { translate };
import {
callPopup,
eventSource,
event_types,
getRequestHeaders,
@ -11,6 +10,7 @@ import {
updateMessageBlock,
} from '../../../script.js';
import { extension_settings, getContext, renderExtensionTemplateAsync } from '../../extensions.js';
import { POPUP_TYPE, callGenericPopup } from '../../popup.js';
import { findSecret, secret_state, writeSecret } from '../../secrets.js';
import { SlashCommand } from '../../slash-commands/SlashCommand.js';
import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from '../../slash-commands/SlashCommandArgument.js';
@ -510,7 +510,7 @@ async function onTranslateChatClick() {
async function onTranslationsClearClick() {
const popupHtml = await renderExtensionTemplateAsync('translate', 'deleteConfirmation');
const confirm = await callPopup(popupHtml, 'confirm');
const confirm = await callGenericPopup(popupHtml, POPUP_TYPE.CONFIRM);
if (!confirm) {
return;
@ -598,7 +598,7 @@ jQuery(async () => {
$(document).on('click', '.mes_translate', onMessageTranslateClick);
$('#translate_key_button').on('click', async () => {
const optionText = $('#translation_provider option:selected').text();
const key = await callPopup(`<h3>${optionText} API Key</h3>`, 'input');
const key = await callGenericPopup(`<h3>${optionText} API Key</h3>`, POPUP_TYPE.INPUT);
if (key == false) {
return;
@ -621,7 +621,7 @@ jQuery(async () => {
const secretKey = extension_settings.translate.provider + '_url';
const savedUrl = secret_state[secretKey] ? await findSecret(secretKey) : '';
const url = await callPopup(popupText, 'input', savedUrl);
const url = await callGenericPopup(popupText, POPUP_TYPE.INPUT, savedUrl);
if (url == false || url == '') {
return;

View File

@ -1,4 +1,4 @@
import { callPopup, cancelTtsPlay, eventSource, event_types, isStreamingEnabled, name2, saveSettingsDebounced, substituteParams } from '../../../script.js';
import { cancelTtsPlay, eventSource, event_types, isStreamingEnabled, name2, saveSettingsDebounced, substituteParams } from '../../../script.js';
import { ModuleWorkerWrapper, doExtrasFetch, extension_settings, getApiUrl, getContext, modules, renderExtensionTemplateAsync } from '../../extensions.js';
import { delay, escapeRegex, getBase64Async, getStringHash, onlyUnique } from '../../utils.js';
import { EdgeTtsProvider } from './edge.js';
@ -21,6 +21,7 @@ import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from '
import { debounce_timeout } from '../../constants.js';
import { SlashCommandEnumValue, enumTypes } from '../../slash-commands/SlashCommandEnumValue.js';
import { enumIcons } from '../../slash-commands/SlashCommandCommonEnumsProvider.js';
import { POPUP_TYPE, callGenericPopup } from '../../popup.js';
export { talkingAnimation };
const UPDATE_INTERVAL = 1000;
@ -310,7 +311,7 @@ async function onTtsVoicesClick() {
popupText = 'Could not load voices list. Check your API key.';
}
callPopup(popupText, 'text');
callGenericPopup(popupText, POPUP_TYPE.TEXT, '', { allowVerticalScrolling: true });
}
function updateUiAudioPlayState() {

View File

@ -24,7 +24,6 @@ import {
characters,
default_avatar,
addOneMessage,
callPopup,
clearChat,
Generate,
select_rm_info,
@ -75,7 +74,7 @@ import {
import { printTagList, createTagMapFromList, applyTagsOnCharacterSelect, tag_map, applyTagsOnGroupSelect } from './tags.js';
import { FILTER_TYPES, FilterHelper } from './filters.js';
import { isExternalMediaAllowed } from './chats.js';
import { POPUP_TYPE, callGenericPopup } from './popup.js';
import { POPUP_TYPE, Popup, callGenericPopup } from './popup.js';
export {
selected_group,
@ -1300,14 +1299,20 @@ function isGroupMemberDisabled(avatarId) {
return Boolean(thisGroup && thisGroup.disabled_members.includes(avatarId));
}
function onDeleteGroupClick() {
async function onDeleteGroupClick() {
if (!openGroupId) {
toastr.warning('Currently no group selected.');
return;
}
if (is_group_generating) {
toastr.warning('Not so fast! Wait for the characters to stop typing before deleting the group.');
return;
}
$('#dialogue_popup').data('group_id', openGroupId);
callPopup('<h3>Delete the group?</h3><p>This will also delete all your chats with that group. If you want to delete a single conversation, select a "View past chats" option in the lower left menu.</p>', 'del_group');
const confirm = await Popup.show.confirm('Delete the group?', '<p>This will also delete all your chats with that group. If you want to delete a single conversation, select a "View past chats" option in the lower left menu.</p>');
if (confirm) {
deleteGroup(openGroupId);
}
}
async function onFavoriteGroupClick() {
@ -1476,8 +1481,7 @@ async function uploadGroupAvatar(event) {
}
async function restoreGroupAvatar() {
const confirm = await callPopup('<h3>Are you sure you want to restore the group avatar?</h3> Your custom image will be deleted, and a collage will be used instead.', 'confirm');
const confirm = await Popup.show.confirm('Are you sure you want to restore the group avatar?', 'Your custom image will be deleted, and a collage will be used instead.');
if (!confirm) {
return;
}

View File

@ -590,7 +590,37 @@ function selectCurrentPersona() {
}
}
async function lockUserNameToChat() {
/**
* Checks if the persona is locked for the current chat.
* @returns {boolean} Whether the persona is locked
*/
function isPersonaLocked() {
return !!chat_metadata['persona'];
}
/**
* Locks or unlocks the persona for the current chat.
* @param {boolean} state Desired lock state
* @returns {Promise<void>}
*/
export async function setPersonaLockState(state) {
return state ? await lockPersona() : await unlockPersona();
}
/**
* Toggle the persona lock state for the current chat.
* @returns {Promise<void>}
*/
export async function togglePersonaLock() {
return isPersonaLocked()
? await unlockPersona()
: await lockPersona();
}
/**
* Unlock the persona for the current chat.
*/
async function unlockPersona() {
if (chat_metadata['persona']) {
console.log(`Unlocking persona for this chat ${chat_metadata['persona']}`);
delete chat_metadata['persona'];
@ -599,9 +629,13 @@ async function lockUserNameToChat() {
toastr.info('User persona is now unlocked for this chat. Click the "Lock" again to revert.', 'Persona unlocked');
}
updateUserLockIcon();
return;
}
}
/**
* Lock the persona for the current chat.
*/
async function lockPersona() {
if (!(user_avatar in power_user.personas)) {
console.log(`Creating a new persona ${user_avatar}`);
if (power_user.persona_show_notifications) {
@ -625,6 +659,7 @@ async function lockUserNameToChat() {
updateUserLockIcon();
}
async function deleteUserAvatar(e) {
e?.stopPropagation();
const avatarId = $(this).closest('.avatar-container').find('.avatar').attr('imgfile');
@ -973,7 +1008,7 @@ export function initPersonas() {
$(document).on('click', '.bind_user_name', bindUserNameToPersona);
$(document).on('click', '.set_default_persona', setDefaultPersona);
$(document).on('click', '.delete_avatar', deleteUserAvatar);
$('#lock_user_name').on('click', lockUserNameToChat);
$('#lock_user_name').on('click', togglePersonaLock);
$('#create_dummy_persona').on('click', createDummyPersona);
$('#persona_description').on('input', onPersonaDescriptionInput);
$('#persona_description_position').on('input', onPersonaDescriptionPositionInput);

View File

@ -74,6 +74,22 @@ const showPopupHelper = {
const value = await popup.show();
return value ? String(value) : null;
},
/**
* Asynchronously displays a confirmation popup with the given header and text, returning the clicked result button value.
*
* @param {string} header - The header text for the popup.
* @param {string} text - The main text for the popup.
* @param {PopupOptions} [popupOptions={}] - Options for the popup.
* @return {Promise<POPUP_RESULT>} A Promise that resolves with the result of the user's interaction.
*/
confirm: async (header, text, popupOptions = {}) => {
const content = PopupUtils.BuildTextWithHeader(header, text);
const popup = new Popup(content, POPUP_TYPE.CONFIRM, null, popupOptions);
const result = await popup.show();
if (typeof result === 'string' || typeof result === 'boolean') throw new Error(`Invalid popup result. CONFIRM popups only support numbers, or null. Result: ${result}`);
return result;
}
};
export class Popup {

View File

@ -2,7 +2,6 @@ import {
saveSettingsDebounced,
scrollChatToBottom,
characters,
callPopup,
reloadMarkdownProcessor,
reloadCurrentChat,
getRequestHeaders,
@ -48,6 +47,7 @@ import { ARGUMENT_TYPE, SlashCommandArgument } from './slash-commands/SlashComma
import { AUTOCOMPLETE_WIDTH } from './autocomplete/AutoComplete.js';
import { SlashCommandEnumValue, enumTypes } from './slash-commands/SlashCommandEnumValue.js';
import { commonEnumProviders, enumIcons } from './slash-commands/SlashCommandCommonEnumsProvider.js';
import { POPUP_TYPE, callGenericPopup } from './popup.js';
export {
loadPowerUserSettings,
@ -1432,7 +1432,7 @@ export function registerDebugFunction(functionId, name, description, func) {
async function showDebugMenu() {
const template = await renderTemplateAsync('debug', { functions: debug_functions });
callPopup(template, 'text', '', { wide: true, large: true });
callGenericPopup(template, POPUP_TYPE.TEXT, '', { wide: true, large: true, allowVerticalScrolling: true });
}
switchUiMode();
@ -2207,7 +2207,8 @@ async function deleteTheme() {
return;
}
const confirm = await callPopup(`Are you sure you want to delete the theme "${themeName}"?`, 'confirm', '', { okButton: 'Yes' });
const template = $(await renderTemplateAsync('themeDelete', { themeName }));
const confirm = await callGenericPopup(template, POPUP_TYPE.CONFIRM);
if (!confirm) {
return;
@ -2269,7 +2270,8 @@ async function importTheme(file) {
}
if (typeof parsed.custom_css === 'string' && parsed.custom_css.includes('@import')) {
const confirm = await callPopup('This theme contains @import lines in the Custom CSS. Press "Yes" to proceed.', 'confirm', '', { okButton: 'Yes' });
const template = $(await renderTemplateAsync('themeImportWarning'));
const confirm = await callGenericPopup(template, POPUP_TYPE.CONFIRM);
if (!confirm) {
throw new Error('Theme contains @import lines');
}
@ -2294,11 +2296,13 @@ async function importTheme(file) {
*/
async function saveTheme(name = undefined, theme = undefined) {
if (typeof name !== 'string') {
name = await callPopup('Enter a theme preset name:', 'input', power_user.theme);
const newName = await callGenericPopup('Enter a theme preset name:', POPUP_TYPE.INPUT, power_user.theme);
if (!name) {
if (!newName) {
return;
}
name = String(newName);
}
if (typeof theme !== 'object') {
@ -2396,7 +2400,7 @@ function getNewTheme(parsed) {
}
async function saveMovingUI() {
const name = await callPopup('Enter a name for the MovingUI Preset:', 'input');
const name = await callGenericPopup('Enter a name for the MovingUI Preset:', POPUP_TYPE.INPUT);
if (!name) {
return;

View File

@ -46,7 +46,7 @@ import { extension_settings, getContext, saveMetadataDebounced } from './extensi
import { getRegexedString, regex_placement } from './extensions/regex/engine.js';
import { findGroupMemberId, getGroupMembers, groups, is_group_generating, openGroupById, resetSelectedGroup, saveGroupChat, selected_group } from './group-chats.js';
import { chat_completion_sources, oai_settings, setupChatCompletionPromptManager } from './openai.js';
import { autoSelectPersona, retriggerFirstMessageOnEmptyChat, user_avatar } from './personas.js';
import { autoSelectPersona, retriggerFirstMessageOnEmptyChat, setPersonaLockState, togglePersonaLock, user_avatar } from './personas.js';
import { addEphemeralStoppingString, chat_styles, flushEphemeralStoppingStrings, power_user } from './power-user.js';
import { textgen_types, textgenerationwebui_settings } from './textgen-settings.js';
import { decodeTextTokens, getFriendlyTokenizerName, getTextTokens, getTokenCountAsync } from './tokenizers.js';
@ -118,9 +118,18 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
}));
SlashCommandParser.addCommandObject(SlashCommand.fromProps({
name: 'lock',
callback: bindCallback,
callback: lockPersonaCallback,
aliases: ['bind'],
helpString: 'Locks/unlocks a persona (name and avatar) to the current chat',
unnamedArgumentList: [
SlashCommandArgument.fromProps({
description: 'state',
typeList: [ARGUMENT_TYPE.STRING],
isRequired: true,
defaultValue: 'toggle',
enumProvider: commonEnumProviders.boolean('onOffToggle'),
}),
],
}));
SlashCommandParser.addCommandObject(SlashCommand.fromProps({
name: 'bg',
@ -1998,6 +2007,9 @@ async function addSwipeCallback(_, arg) {
lastMessage.swipe_info = [{}];
lastMessage.swipe_id = 0;
}
if (!Array.isArray(lastMessage.swipe_info)) {
lastMessage.swipe_info = lastMessage.swipes.map(() => ({}));
}
lastMessage.swipes.push(arg);
lastMessage.swipe_info.push({
@ -2573,8 +2585,23 @@ function syncCallback() {
return '';
}
function bindCallback() {
$('#lock_user_name').trigger('click');
async function lockPersonaCallback(_args, value) {
if (['toggle', 't', ''].includes(value.trim().toLowerCase())) {
await togglePersonaLock();
return '';
}
if (isTrueBoolean(value)) {
await setPersonaLockState(true);
return '';
}
if (isFalseBoolean(value)) {
await setPersonaLockState(false);
return '';
}
return '';
}
@ -2700,9 +2727,26 @@ export async function sendMessageAs(args, text) {
bias: bias.trim().length ? bias : null,
gen_id: Date.now(),
isSmallSys: compact,
api: 'manual',
model: 'slash command',
},
};
message.swipe_id = 0;
message.swipes = [message.mes];
message.swipes_info = [{
send_date: message.send_date,
gen_started: null,
gen_finished: null,
extra: {
bias: message.extra.bias,
gen_id: message.extra.gen_id,
isSmallSys: compact,
api: 'manual',
model: 'slash command',
},
}];
const insertAt = Number(resolveVariable(args.at));
if (!isNaN(insertAt) && insertAt >= 0 && insertAt <= chat.length) {
@ -2745,6 +2789,8 @@ export async function sendNarratorMessage(args, text) {
bias: bias.trim().length ? bias : null,
gen_id: Date.now(),
isSmallSys: compact,
api: 'manual',
model: 'slash command',
},
};
@ -2795,6 +2841,8 @@ export async function promptQuietForLoudResponse(who, text) {
extra: {
type: system_message_types.COMMENT,
gen_id: Date.now(),
api: 'manual',
model: 'slash command',
},
};
@ -2823,6 +2871,8 @@ async function sendCommentMessage(args, text) {
type: system_message_types.COMMENT,
gen_id: Date.now(),
isSmallSys: compact,
api: 'manual',
model: 'slash command',
},
};

View File

@ -1,25 +1,27 @@
<h3 data-i18n="Debug Menu">Debug Menu</h3>
<div data-i18n="Debug Warning">
Functions in this category are for advanced users only. Don't click anything if you're not sure about the consequences.
</div>
<table id="debug_table" class="responsiveTable">
{{#each functions}}
{{#with this}}
<tr>
<td>
<div class="justifyLeft">
<b>{{this.name}}</b>
</div>
<div class="justifyLeft">
{{this.description}}
</div>
<div class="flex-container justifyCenter">
<div class="menu_button menu_button_icon" data-debug-function="{{this.functionId}}" data-i18n="Execute">
Execute
<div>
<h3 data-i18n="Debug Menu">Debug Menu</h3>
<div data-i18n="Debug Warning">
Functions in this category are for advanced users only. Don't click anything if you're not sure about the consequences.
</div>
<table id="debug_table" class="responsiveTable">
{{#each functions}}
{{#with this}}
<tr>
<td>
<div class="justifyLeft">
<b>{{this.name}}</b>
</div>
</div>
</td>
</tr>
{{/with}}
{{/each}}
</table>
<div class="justifyLeft">
{{this.description}}
</div>
<div class="flex-container justifyCenter">
<div class="menu_button menu_button_icon" data-debug-function="{{this.functionId}}" data-i18n="Execute">
Execute
</div>
</div>
</td>
</tr>
{{/with}}
{{/each}}
</table>
</div>

View File

@ -127,3 +127,6 @@ API Used: {{this_main_api}}<br>
</div>
</div>
<hr>
<div id="rawPromptPopup" class="list-group">
<div id="rawPromptWrapper" class="tokenItemizingSubclass"></div>
</div>

View File

@ -107,3 +107,6 @@ API Used: {{this_main_api}}<br>
</div>
</div>
<hr>
<div id="rawPromptPopup" class="list-group">
<div id="rawPromptWrapper" class="tokenItemizingSubclass"></div>
</div>

View File

@ -0,0 +1,3 @@
<div>
Are you sure you want to delete the theme "{{themeName}}"?
</div>

View File

@ -0,0 +1,3 @@
<div>
This theme contains @import lines in the Custom CSS. Press "Yes" to proceed.
</div>

View File

@ -1,8 +1,9 @@
import { getContext } from './extensions.js';
import { callPopup, getRequestHeaders } from '../script.js';
import { getRequestHeaders } from '../script.js';
import { isMobile } from './RossAscends-mods.js';
import { collapseNewlines } from './power-user.js';
import { debounce_timeout } from './constants.js';
import { Popup } from './popup.js';
/**
* Pagination status string template.
@ -1821,7 +1822,7 @@ export async function checkOverwriteExistingData(type, existingNames, name, { in
return true;
}
const overwrite = interactive ? await callPopup(`<h3>${type} ${actionName}</h3><p>A ${type.toLowerCase()} with the same name already exists:<br />${existing}</p>Do you want to overwrite it?`, 'confirm') : false;
const overwrite = interactive && await Popup.show.confirm(`${type} ${actionName}`, `<p>A ${type.toLowerCase()} with the same name already exists:<br />${existing}</p>Do you want to overwrite it?`);
if (!overwrite) {
toastr.warning(`${type} ${actionName.toLowerCase()} cancelled. A ${type.toLowerCase()} with the same name already exists:<br />${existing}`, `${type} ${actionName}`, { escapeHtml: false });
return false;

View File

@ -16,6 +16,7 @@ import { SlashCommandEnumValue, enumTypes } from './slash-commands/SlashCommandE
import { commonEnumProviders, enumIcons } from './slash-commands/SlashCommandCommonEnumsProvider.js';
import { SlashCommandExecutor } from './slash-commands/SlashCommandExecutor.js';
import { SlashCommandClosure } from './slash-commands/SlashCommandClosure.js';
import { Popup } from './popup.js';
export {
world_info,
@ -1684,8 +1685,7 @@ function displayWorldEntries(name, data, navigation = navigation_option.none, fl
// Regardless of whether success is displayed or not. Make sure the delete button is available.
// Do not put this code behind.
$('#world_popup_delete').off('click').on('click', async () => {
const confirmation = await callPopup(`<h3>Delete the World/Lorebook: "${name}"?</h3>This action is irreversible!`, 'confirm');
const confirmation = await Popup.show.confirm(`Delete the World/Lorebook: "${name}"?`, `This action is irreversible!`);
if (!confirmation) {
return;
}
@ -1862,7 +1862,7 @@ function displayWorldEntries(name, data, navigation = navigation_option.none, fl
$('#world_duplicate').off('click').on('click', async () => {
const tempName = getFreeWorldName();
const finalName = await callPopup('<h3>Create a new World Info?</h3>Enter a name for the new file:', 'input', tempName);
const finalName = await Popup.show.input('Create a new World Info?', 'Enter a name for the new file:', tempName);
if (finalName) {
await saveWorldInfo(finalName, data, true);
@ -3190,7 +3190,7 @@ async function saveWorldInfo(name, data, immediately) {
async function renameWorldInfo(name, data) {
const oldName = name;
const newName = await callPopup('<h3>Rename World Info</h3>Enter a new name:', 'input', oldName);
const newName = await Popup.show.input('Rename World Info', 'Enter a new name:', oldName);
if (oldName === newName || !newName) {
console.debug('World info rename cancelled');
@ -4138,11 +4138,9 @@ export async function importEmbeddedWorldInfo(skipPopup = false) {
}
const bookName = characters[chid]?.data?.character_book?.name || `${characters[chid]?.name}'s Lorebook`;
const confirmationText = (`<h3>Are you sure you want to import "${bookName}"?</h3>`) + (world_names.includes(bookName) ? 'It will overwrite the World/Lorebook with the same name.' : '');
if (!skipPopup) {
const confirmation = await callPopup(confirmationText, 'confirm');
const confirmation = await Popup.show.confirm(`Are you sure you want to import "${bookName}"?`, world_names.includes(bookName) ? 'It will overwrite the World/Lorebook with the same name.' : '');
if (!confirmation) {
return;
}
@ -4382,7 +4380,7 @@ jQuery(() => {
$('#world_create_button').on('click', async () => {
const tempName = getFreeWorldName();
const finalName = await callPopup('<h3>Create a new World Info?</h3>Enter a name for the new file:', 'input', tempName);
const finalName = await Popup.show.input('Create a new World Info', 'Enter a name for the new file:', tempName);
if (finalName) {
await createNewWorldInfo(finalName, { interactive: true });

View File

@ -82,7 +82,7 @@
/*base variable calculated in rems*/
--fontScale: 1;
--mainFontSize: calc(var(--fontScale) * 15px);
--mainFontFamily: "Noto Sans", "Noto Color Emoji", sans-serif;
--mainFontFamily: "Noto Sans", sans-serif;
--monoFontFamily: 'Noto Sans Mono', 'Courier New', Consolas, monospace;
/* base variable for blur strength slider calculations */
@ -256,10 +256,6 @@ input[type='checkbox']:focus-visible {
color: var(--SmartThemeEmColor);
}
#rawPromptWrapper {
white-space: pre-wrap;
}
.tokenGraph {
border-radius: 10px;
border: 1px solid var(--SmartThemeBorderColor);
@ -462,7 +458,7 @@ code {
kbd {
display: inline-block;
padding: 2px 4px;
font-family: Consolas, monospace;
font-family: var(--monoFontFamily);
white-space: nowrap;
/* background-color: #eeeeee; */
background-color: rgba(255, 255, 255, 0.9);
@ -4367,8 +4363,7 @@ a {
text-decoration: none;
}
#export_format_popup,
#rawPromptPopup {
#export_format_popup {
display: none;
z-index: 9999;
}
@ -4376,7 +4371,7 @@ a {
#rawPromptPopup {
inset: 0px auto auto 0px;
margin: 0px;
transform: translate(909px, 47px);
transform: translate(500px, 0px);
display: block;
overflow-wrap: break-word;
white-space: normal;
@ -4395,7 +4390,8 @@ a {
display: none;
}
#rawPopupWrapper {
#rawPromptWrapper {
white-space: pre-wrap;
word-wrap: break-word;
width: 100%;
text-align: start;