mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
Streamline persona toasts, infos and states
- Added "info" block to persona description panel to show when a temporary persona is in use. Hide the long text behind a tooltip - Reduce toast spam even further, not showing toasts when persona panel is open - Restyle connection state buttons a bit - Designed common 'info-block' utility to show markdown-like info blocks, with CSS styling
This commit is contained in:
@ -5002,7 +5002,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h4 data-i18n="Connections">Connections</h4>
|
<h4 data-i18n="Connections">Connections</h4>
|
||||||
<div id="persona_connections_buttons" class="flex-container marginBot10">
|
<div id="persona_connections_buttons" class="flex-container">
|
||||||
<div id="lock_persona_default" class="menu_button menu_button_icon" title="Click to select this as default persona for the new chats. Click again to remove it." data-i18n="[title]Click to select this as default persona for the new chats. Click again to remove it.">
|
<div id="lock_persona_default" class="menu_button menu_button_icon" title="Click to select this as default persona for the new chats. Click again to remove it." data-i18n="[title]Click to select this as default persona for the new chats. Click again to remove it.">
|
||||||
<i class="icon fa-solid fa-crown fa-fw"></i>
|
<i class="icon fa-solid fa-crown fa-fw"></i>
|
||||||
<div data-i18n="Default">Default</div>
|
<div data-i18n="Default">Default</div>
|
||||||
@ -5016,6 +5016,7 @@
|
|||||||
<div data-i18n="Character">Character</div>
|
<div data-i18n="Character">Character</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="persona_connections_info_block"></div>
|
||||||
<div id="persona_connections_list" class="text_muted m-b-1 avatars_inline avatars_multiline scroll-reset-container expander">
|
<div id="persona_connections_list" class="text_muted m-b-1 avatars_inline avatars_multiline scroll-reset-container expander">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -237,6 +237,7 @@ import {
|
|||||||
getConnectedPersonas,
|
getConnectedPersonas,
|
||||||
askForPersonaSelection,
|
askForPersonaSelection,
|
||||||
getCurrentConnectionObj,
|
getCurrentConnectionObj,
|
||||||
|
isPersonaPanelOpen,
|
||||||
} from './scripts/personas.js';
|
} from './scripts/personas.js';
|
||||||
import { getBackgrounds, initBackgrounds, loadBackgroundSettings, background_settings } from './scripts/backgrounds.js';
|
import { getBackgrounds, initBackgrounds, loadBackgroundSettings, background_settings } from './scripts/backgrounds.js';
|
||||||
import { hideLoader, showLoader } from './scripts/loader.js';
|
import { hideLoader, showLoader } from './scripts/loader.js';
|
||||||
@ -6811,7 +6812,7 @@ export function setUserName(value, { toastPersonaNameChange = true } = {}) {
|
|||||||
name1 = default_user_name;
|
name1 = default_user_name;
|
||||||
console.log(`User name changed to ${name1}`);
|
console.log(`User name changed to ${name1}`);
|
||||||
$('#your_name').text(name1);
|
$('#your_name').text(name1);
|
||||||
if (toastPersonaNameChange && power_user.persona_show_notifications) {
|
if (toastPersonaNameChange && power_user.persona_show_notifications && !isPersonaPanelOpen()) {
|
||||||
toastr.success(t`Your messages will now be sent as ${name1}`, t`Persona Changed`);
|
toastr.success(t`Your messages will now be sent as ${name1}`, t`Persona Changed`);
|
||||||
}
|
}
|
||||||
saveSettingsDebounced();
|
saveSettingsDebounced();
|
||||||
|
@ -21,7 +21,7 @@ import {
|
|||||||
} from '../script.js';
|
} from '../script.js';
|
||||||
import { persona_description_positions, power_user } from './power-user.js';
|
import { persona_description_positions, power_user } from './power-user.js';
|
||||||
import { getTokenCountAsync } from './tokenizers.js';
|
import { getTokenCountAsync } from './tokenizers.js';
|
||||||
import { PAGINATION_TEMPLATE, debounce, delay, download, ensureImageFormatSupported, flashHighlight, getBase64Async, getCharIndex, onlyUnique, parseJsonFile } from './utils.js';
|
import { PAGINATION_TEMPLATE, clearInfoBlock, debounce, delay, download, ensureImageFormatSupported, flashHighlight, getBase64Async, getCharIndex, onlyUnique, parseJsonFile, setInfoBlock } from './utils.js';
|
||||||
import { debounce_timeout } from './constants.js';
|
import { debounce_timeout } from './constants.js';
|
||||||
import { FILTER_TYPES, FilterHelper } from './filters.js';
|
import { FILTER_TYPES, FilterHelper } from './filters.js';
|
||||||
import { groups, selected_group } from './group-chats.js';
|
import { groups, selected_group } from './group-chats.js';
|
||||||
@ -57,6 +57,15 @@ const DEFAULT_ROLE = 0;
|
|||||||
export let user_avatar = '';
|
export let user_avatar = '';
|
||||||
export const personasFilter = new FilterHelper(debounce(getUserAvatars, debounce_timeout.quick));
|
export const personasFilter = new FilterHelper(debounce(getUserAvatars, debounce_timeout.quick));
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the Persona Management panel is currently open
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
export function isPersonaPanelOpen() {
|
||||||
|
return document.querySelector('#persona-management-button .drawer-content')?.classList.contains('openDrawer') ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
function switchPersonaGridView() {
|
function switchPersonaGridView() {
|
||||||
const state = localStorage.getItem(GRID_STORAGE_KEY) === 'true';
|
const state = localStorage.getItem(GRID_STORAGE_KEY) === 'true';
|
||||||
$('#user_avatar_block').toggleClass('gridView', state);
|
$('#user_avatar_block').toggleClass('gridView', state);
|
||||||
@ -795,17 +804,14 @@ function selectCurrentPersona({ toastPersonaNameChange = true } = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// As the last step, inform user if the persona is only temporarily chosen
|
// As the last step, inform user if the persona is only temporarily chosen
|
||||||
if (power_user.persona_show_notifications) {
|
if (power_user.persona_show_notifications && !isPersonaPanelOpen()) {
|
||||||
const hasDifferentChatLock = !!chat_metadata['persona'] && chat_metadata['persona'] !== user_avatar;
|
const temporary = getPersonaTemporaryLockInfo();
|
||||||
const hasDifferentDefaultLock = power_user.default_persona && power_user.default_persona !== user_avatar;
|
if (temporary.isTemporary) {
|
||||||
|
toastr.info(t`This persona is only temporarily chosen. Click for more info.`, t`Temporary Persona`, {
|
||||||
if (hasDifferentChatLock || (!chat_metadata['persona'] && hasDifferentDefaultLock)) {
|
preventDuplicates: true, onclick: () => {
|
||||||
const message = t`A different persona is locked to this chat, or you have a different default persona set. The currently selected persona will only be temporary, and resets on reload. Consider locking this persona to the chat if you want to permanently use it.`
|
toastr.info(temporary.info.replaceAll('\n', '<br />'), t`Temporary Persona`, { escapeHtml: false });
|
||||||
+ '<br /><br />'
|
},
|
||||||
+ t`Current Persona: ${power_user.personas[user_avatar]}`
|
});
|
||||||
+ (hasDifferentChatLock ? '<br />' + t`Chat persona: ${power_user.personas[chat_metadata['persona']]}` : '')
|
|
||||||
+ (hasDifferentDefaultLock ? '<br />' + t`Default persona: ${power_user.personas[power_user.default_persona]}` : '');
|
|
||||||
toastr.info(message, t`Temporary Persona`, { escapeHtml: false, preventDuplicates: true });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -881,7 +887,7 @@ async function unlockPersona(type = 'chat') {
|
|||||||
console.log(`Unlocking persona ${user_avatar} from this chat`);
|
console.log(`Unlocking persona ${user_avatar} from this chat`);
|
||||||
delete chat_metadata['persona'];
|
delete chat_metadata['persona'];
|
||||||
await saveMetadata();
|
await saveMetadata();
|
||||||
if (power_user.persona_show_notifications) {
|
if (power_user.persona_show_notifications && !isPersonaPanelOpen()) {
|
||||||
toastr.info(t`Persona ${name1} is now unlocked from this chat.`, t`Persona Unlocked`);
|
toastr.info(t`Persona ${name1} is now unlocked from this chat.`, t`Persona Unlocked`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -895,7 +901,7 @@ async function unlockPersona(type = 'chat') {
|
|||||||
power_user.persona_descriptions[user_avatar].connections = connections.filter(c => !isPersonaConnectionLocked(c));
|
power_user.persona_descriptions[user_avatar].connections = connections.filter(c => !isPersonaConnectionLocked(c));
|
||||||
saveSettingsDebounced();
|
saveSettingsDebounced();
|
||||||
updatePersonaConnectionsAvatarList();
|
updatePersonaConnectionsAvatarList();
|
||||||
if (power_user.persona_show_notifications) {
|
if (power_user.persona_show_notifications && !isPersonaPanelOpen()) {
|
||||||
toastr.info(t`Persona ${name1} is now unlocked from character ${name2}.`, t`Persona Unlocked`);
|
toastr.info(t`Persona ${name1} is now unlocked from character ${name2}.`, t`Persona Unlocked`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -939,7 +945,7 @@ async function lockPersona(type = 'chat') {
|
|||||||
console.log(`Locking persona ${user_avatar} to this chat`);
|
console.log(`Locking persona ${user_avatar} to this chat`);
|
||||||
chat_metadata['persona'] = user_avatar;
|
chat_metadata['persona'] = user_avatar;
|
||||||
saveMetadataDebounced();
|
saveMetadataDebounced();
|
||||||
if (power_user.persona_show_notifications) {
|
if (power_user.persona_show_notifications && !isPersonaPanelOpen()) {
|
||||||
toastr.success(t`User persona ${name1} is locked to ${name2} in this chat`, t`Persona Locked`);
|
toastr.success(t`User persona ${name1} is locked to ${name2} in this chat`, t`Persona Locked`);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -971,7 +977,9 @@ async function lockPersona(type = 'chat') {
|
|||||||
let additional = '';
|
let additional = '';
|
||||||
if (unlinkedCharacters.length)
|
if (unlinkedCharacters.length)
|
||||||
additional += `<br /><br />${t`Unlinked existing persona${unlinkedCharacters.length > 1 ? 's' : ''}: ${unlinkedCharacters.join(', ')}`}`;
|
additional += `<br /><br />${t`Unlinked existing persona${unlinkedCharacters.length > 1 ? 's' : ''}: ${unlinkedCharacters.join(', ')}`}`;
|
||||||
toastr.success(t`User persona ${name1} is locked to character ${name2}${additional}`, t`Persona Locked`, { escapeHtml: false });
|
if (additional || !isPersonaPanelOpen()) {
|
||||||
|
toastr.success(t`User persona ${name1} is locked to character ${name2}${additional}`, t`Persona Locked`, { escapeHtml: false });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -1175,7 +1183,7 @@ async function toggleDefaultPersonaClicked(e) {
|
|||||||
* @param {boolean} [options.quiet=false] If true, no confirmation popups will be shown
|
* @param {boolean} [options.quiet=false] If true, no confirmation popups will be shown
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
async function toggleDefaultPersona(avatarId, { quiet: quiet = false } = {}) {
|
async function toggleDefaultPersona(avatarId, { quiet = false } = {}) {
|
||||||
if (!avatarId) {
|
if (!avatarId) {
|
||||||
console.warn('No avatar id found');
|
console.warn('No avatar id found');
|
||||||
return;
|
return;
|
||||||
@ -1200,7 +1208,7 @@ async function toggleDefaultPersona(avatarId, { quiet: quiet = false } = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
console.log(`Removing default persona ${avatarId}`);
|
console.log(`Removing default persona ${avatarId}`);
|
||||||
if (power_user.persona_show_notifications) {
|
if (power_user.persona_show_notifications && !isPersonaPanelOpen()) {
|
||||||
toastr.info(t`This persona will no longer be used by default when you open a new chat.`, t`Default Persona Removed`);
|
toastr.info(t`This persona will no longer be used by default when you open a new chat.`, t`Default Persona Removed`);
|
||||||
}
|
}
|
||||||
delete power_user.default_persona;
|
delete power_user.default_persona;
|
||||||
@ -1217,7 +1225,7 @@ async function toggleDefaultPersona(avatarId, { quiet: quiet = false } = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
power_user.default_persona = avatarId;
|
power_user.default_persona = avatarId;
|
||||||
if (power_user.persona_show_notifications) {
|
if (power_user.persona_show_notifications && !isPersonaPanelOpen()) {
|
||||||
toastr.success(t`Set to ${power_user.personas[avatarId]}.This persona will be used by default when you open a new chat.`, t`Default Persona`);
|
toastr.success(t`Set to ${power_user.personas[avatarId]}.This persona will be used by default when you open a new chat.`, t`Default Persona`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1280,6 +1288,53 @@ function updatePersonaUIStates() {
|
|||||||
$('#lock_persona_to_char').toggleClass('locked', personaStates.locked.character);
|
$('#lock_persona_to_char').toggleClass('locked', personaStates.locked.character);
|
||||||
$('#lock_persona_to_char i.icon').toggleClass('fa-lock', personaStates.locked.character);
|
$('#lock_persona_to_char i.icon').toggleClass('fa-lock', personaStates.locked.character);
|
||||||
$('#lock_persona_to_char i.icon').toggleClass('fa-unlock', !personaStates.locked.character);
|
$('#lock_persona_to_char i.icon').toggleClass('fa-unlock', !personaStates.locked.character);
|
||||||
|
|
||||||
|
// Persona panel info block
|
||||||
|
const { isTemporary, info } = getPersonaTemporaryLockInfo();
|
||||||
|
if (isTemporary) {
|
||||||
|
const messageContainer = document.createElement('div');
|
||||||
|
messageContainer.innerHTML = t`Temporary persona in use.`;
|
||||||
|
|
||||||
|
const infoIcon = document.createElement('i');
|
||||||
|
infoIcon.classList.add('fa-solid', 'fa-circle-info', 'opacity50p', 'marginLeft5');
|
||||||
|
infoIcon.title = info;
|
||||||
|
messageContainer.appendChild(infoIcon);
|
||||||
|
|
||||||
|
// Set the info block content
|
||||||
|
setInfoBlock('#persona_connections_info_block', messageContainer, 'hint');
|
||||||
|
} else {
|
||||||
|
// Clear the info block if no condition applies
|
||||||
|
clearInfoBlock('#persona_connections_info_block');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the currently selected persona is temporary due to either a different default persona
|
||||||
|
* or a different persona being locked to the current chat. If so, it also returns a string that
|
||||||
|
* can be used to describe this situation to the user.
|
||||||
|
*
|
||||||
|
* @returns {{isTemporary: boolean, hasDifferentChatLock: boolean, hasDifferentDefaultLock: boolean, info: string?}} An object containing 4 properties:
|
||||||
|
* - isTemporary: A boolean indicating if the current persona is temporary
|
||||||
|
* - hasDifferentChatLock: A boolean indicating if the current chat has a different persona locked to it
|
||||||
|
* - hasDifferentDefaultLock: A boolean indicating if there is a different default persona set
|
||||||
|
* - info: A string describing the situation, or an empty if not temporary
|
||||||
|
*/
|
||||||
|
function getPersonaTemporaryLockInfo() {
|
||||||
|
const hasDifferentChatLock = !!chat_metadata['persona'] && chat_metadata['persona'] !== user_avatar;
|
||||||
|
const hasDifferentDefaultLock = power_user.default_persona && power_user.default_persona !== user_avatar;
|
||||||
|
const isTemporary = hasDifferentChatLock || (!chat_metadata['persona'] && hasDifferentDefaultLock);
|
||||||
|
const info = isTemporary ? t`A different persona is locked to this chat, or you have a different default persona set. The currently selected persona will only be temporary, and resets on reload. Consider locking this persona to the chat if you want to permanently use it.`
|
||||||
|
+ '\n\n'
|
||||||
|
+ t`Current Persona: ${power_user.personas[user_avatar]}`
|
||||||
|
+ (hasDifferentChatLock ? '\n' + t`Chat persona: ${power_user.personas[chat_metadata['persona']]}` : '')
|
||||||
|
+ (hasDifferentDefaultLock ? '\n' + t`Default persona: ${power_user.personas[power_user.default_persona]}` : '') : '';
|
||||||
|
|
||||||
|
return {
|
||||||
|
isTemporary: isTemporary,
|
||||||
|
hasDifferentChatLock: hasDifferentChatLock,
|
||||||
|
hasDifferentDefaultLock: hasDifferentDefaultLock,
|
||||||
|
info: info,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadPersonaForCurrentChat({ doRender = false } = {}) {
|
async function loadPersonaForCurrentChat({ doRender = false } = {}) {
|
||||||
|
@ -2221,3 +2221,34 @@ export function arraysEqual(a, b) {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the content and style of an information block
|
||||||
|
* @param {string | HTMLElement} target - The CSS selector or the HTML element of the information block
|
||||||
|
* @param {string | HTMLElement} content - The message to display inside the information block (supports HTML) or an HTML element
|
||||||
|
* @param {'hint' | 'info' | 'warning' | 'error'} [type='info'] - The type of message, which determines the styling of the information block
|
||||||
|
*/
|
||||||
|
export function setInfoBlock(target, content, type = 'info') {
|
||||||
|
const infoBlock = typeof target === 'string' ? document.querySelector(target) : target;
|
||||||
|
if (infoBlock) {
|
||||||
|
infoBlock.className = `info-block ${type}`;
|
||||||
|
if (typeof content === 'string') {
|
||||||
|
infoBlock.innerHTML = content;
|
||||||
|
} else {
|
||||||
|
infoBlock.innerHTML = '';
|
||||||
|
infoBlock.appendChild(content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the content and style of an information block.
|
||||||
|
* @param {string | HTMLElement} target - The CSS selector or the HTML element of the information block
|
||||||
|
*/
|
||||||
|
export function clearInfoBlock(target) {
|
||||||
|
const infoBlock = typeof target === 'string' ? document.querySelector(target) : target;
|
||||||
|
if (infoBlock && infoBlock.classList.contains('info-block')) {
|
||||||
|
infoBlock.className = '';
|
||||||
|
infoBlock.innerHTML = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -2961,6 +2961,10 @@ select option:not(:checked) {
|
|||||||
color: var(--grey50);
|
color: var(--grey50);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#persona_connections_buttons {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
input[type=search]::-webkit-search-cancel-button {
|
input[type=search]::-webkit-search-cancel-button {
|
||||||
-webkit-appearance: none;
|
-webkit-appearance: none;
|
||||||
height: 1em;
|
height: 1em;
|
||||||
@ -3912,6 +3916,14 @@ input[type='checkbox'].del_checkbox {
|
|||||||
color: var(--active);
|
color: var(--active);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#lock_user_name.locked {
|
||||||
|
border-color: color-mix(in srgb, var(--SmartThemeQuoteColor) 50%, var(--SmartThemeBorderColor));;
|
||||||
|
}
|
||||||
|
|
||||||
|
#lock_persona_to_char.locked {
|
||||||
|
border-color: color-mix(in srgb, var(--active) 50%, var(--SmartThemeBorderColor));
|
||||||
|
}
|
||||||
|
|
||||||
#user_avatar_block .avatar_upload {
|
#user_avatar_block .avatar_upload {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
width: 60px;
|
width: 60px;
|
||||||
@ -5922,3 +5934,36 @@ body:not(.movingUI) .drawer-content.maximized {
|
|||||||
.multiline {
|
.multiline {
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.info-block {
|
||||||
|
padding: 10px 1em;
|
||||||
|
margin: 1em 0;
|
||||||
|
border-radius: 10px;
|
||||||
|
border-left: 5px solid;
|
||||||
|
background-color: var(--SmartThemeBlurTintColor);
|
||||||
|
color: var(--SmartThemeBodyColor);
|
||||||
|
backdrop-filter: blur(calc(var(--SmartThemeBlurStrength)));
|
||||||
|
-webkit-backdrop-filter: blur(calc(var(--SmartThemeBlurStrength)));
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-block.hint {
|
||||||
|
border-color: var(--info-color, #7fb3e8);
|
||||||
|
background-color: rgba(163, 201, 241, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-block.info {
|
||||||
|
border-color: var(--default-color, #e8e07f);
|
||||||
|
background-color: rgba(255, 255, 224, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-block.warning {
|
||||||
|
border-color: var(--warning-color, #e8a97f);
|
||||||
|
background-color: rgba(241, 198, 163, 0.2);
|
||||||
|
/* Reset the font weight from that main warning class */
|
||||||
|
font-weight: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-block.error {
|
||||||
|
border-color: var(--error-color, #e87f7f);
|
||||||
|
background-color: rgba(241, 163, 163, 0.2);
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user