Merge branch 'staging' of https://github.com/Yokayo/SillyTavern into staging

This commit is contained in:
Yokayo 2024-10-06 23:52:46 +07:00
commit 88d52ea587
11 changed files with 202 additions and 104 deletions

View File

@ -6639,7 +6639,7 @@
<script src="lib/popper.js"></script>
<script src="lib/purify.min.js"></script>
<script src="lib/highlight.min.js"></script>
<script src="lib/moment.min.js"></script>
<script src="lib/moment-with-locales.min.js"></script>
<script src="lib/cropper.min.js"></script>
<script src="lib/jquery-cropper.min.js"></script>
<script src="lib/toastr.min.js"></script>

2
public/lib/moment-with-locales.min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -293,10 +293,17 @@ DOMPurify.addHook('afterSanitizeAttributes', function (node) {
}
});
DOMPurify.addHook('uponSanitizeAttribute', (_, data, config) => {
DOMPurify.addHook('uponSanitizeAttribute', (node, data, config) => {
if (!config['MESSAGE_SANITIZE']) {
return;
}
/* Retain the classes on UI elements of messages that interact with the main UI */
const permittedNodeTypes = ['BUTTON', 'DIV'];
if (config['MESSAGE_ALLOW_SYSTEM_UI'] && node.classList.contains('menu_button') && permittedNodeTypes.includes(node.nodeName)) {
return;
}
switch (data.attrName) {
case 'class': {
if (data.attrValue) {
@ -650,6 +657,7 @@ async function getSystemMessages() {
force_avatar: system_avatar,
is_user: false,
is_system: true,
uses_system_ui: true,
mes: await renderTemplateAsync('welcome', { displayVersion }),
},
group: {
@ -1916,9 +1924,10 @@ export async function sendTextareaMessage() {
* @param {boolean} isSystem If the message was sent by the system
* @param {boolean} isUser If the message was sent by the user
* @param {number} messageId Message index in chat array
* @param {object} [sanitizerOverrides] DOMPurify sanitizer option overrides
* @returns {string} HTML string
*/
export function messageFormatting(mes, ch_name, isSystem, isUser, messageId) {
export function messageFormatting(mes, ch_name, isSystem, isUser, messageId, sanitizerOverrides = {}) {
if (!mes) {
return '';
}
@ -1993,15 +2002,33 @@ export function messageFormatting(mes, ch_name, isSystem, isUser, messageId) {
});
}
mes = mes.replace(/```[\s\S]*?```|``[\s\S]*?``|`[\s\S]*?`|(".+?")|(\u201C.+?\u201D)/gm, function (match, p1, p2) {
if (p1) {
return '<q>"' + p1.replace(/"/g, '') + '"</q>';
} else if (p2) {
return '<q>“' + p2.replace(/\u201C|\u201D/g, '') + '”</q>';
} else {
return match;
mes = mes.replace(
/```[\s\S]*?```|``[\s\S]*?``|`[\s\S]*?`|(".*?")|(\u201C.*?\u201D)|(\u00AB.*?\u00BB)|(\u300C.*?\u300D)|(\u300E.*?\u300F)|(\uFF02.*?\uFF02)/gm,
function (match, p1, p2, p3, p4, p5, p6) {
if (p1) {
// English double quotes
return `<q>"${p1.slice(1, -1)}"</q>`;
} else if (p2) {
// Curly double quotes “ ”
return `<q>“${p2.slice(1, -1)}”</q>`;
} else if (p3) {
// Guillemets « »
return `<q>«${p3.slice(1, -1)}»</q>`;
} else if (p4) {
// Corner brackets 「 」
return `<q>「${p4.slice(1, -1)}」</q>`;
} else if (p5) {
// White corner brackets 『 』
return `<q>『${p5.slice(1, -1)}』</q>`;
} else if (p6) {
// Fullwidth quotes
return `<q>${p6.slice(1, -1)}</q>`;
} else {
// Return the original match if no quotes are found
return match;
}
}
});
);
// Restore double quotes in tags
if (!power_user.encode_tags) {
@ -2029,7 +2056,7 @@ export function messageFormatting(mes, ch_name, isSystem, isUser, messageId) {
}
/** @type {any} */
const config = { MESSAGE_SANITIZE: true, ADD_TAGS: ['custom-style'] };
const config = { MESSAGE_SANITIZE: true, ADD_TAGS: ['custom-style'], ...sanitizerOverrides };
mes = encodeStyleTags(mes);
mes = DOMPurify.sanitize(mes, config);
mes = decodeStyleTags(mes);
@ -2234,6 +2261,18 @@ export function addCopyToCodeBlocks(messageElement) {
}
/**
* Adds a single message to the chat.
* @param {object} mes Message object
* @param {object} [options] Options
* @param {string} [options.type='normal'] Message type
* @param {number} [options.insertAfter=null] Message ID to insert the new message after
* @param {boolean} [options.scroll=true] Whether to scroll to the new message
* @param {number} [options.insertBefore=null] Message ID to insert the new message before
* @param {number} [options.forceId=null] Force the message ID
* @param {boolean} [options.showSwipes=true] Whether to show swipe buttons
* @returns {void}
*/
export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll = true, insertBefore = null, forceId = null, showSwipes = true } = {}) {
let messageText = mes['mes'];
const momentDate = timestampToMoment(mes.send_date);
@ -2262,7 +2301,7 @@ export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll
} else if (this_chid === undefined) {
avatarImg = system_avatar;
} else {
if (characters[this_chid].avatar != 'none') {
if (characters[this_chid].avatar !== 'none') {
avatarImg = getThumbnailUrl('avatar', characters[this_chid].avatar);
} else {
avatarImg = default_avatar;
@ -2277,12 +2316,16 @@ export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll
avatarImg = mes['force_avatar'];
}
// if mes.uses_system_ui is true, set an override on the sanitizer options
const sanitizerOverrides = mes.uses_system_ui ? { MESSAGE_ALLOW_SYSTEM_UI: true } : {};
messageText = messageFormatting(
messageText,
mes.name,
isSystem,
mes.is_user,
chat.indexOf(mes),
sanitizerOverrides,
);
const bias = messageFormatting(mes.extra?.bias ?? '', '', false, false, -1);
let bookmarkLink = mes?.extra?.bookmark_link ?? '';
@ -2330,7 +2373,7 @@ export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll
}
//shows or hides the Prompt display button
let mesIdToFind = type == 'swipe' ? params.mesId - 1 : params.mesId; //Number(newMessage.attr('mesId'));
let mesIdToFind = type === 'swipe' ? params.mesId - 1 : params.mesId; //Number(newMessage.attr('mesId'));
//if we have itemized messages, and the array isn't null..
if (params.isUser === false && Array.isArray(itemizedPrompts) && itemizedPrompts.length > 0) {
@ -2651,7 +2694,7 @@ export function sendSystemMessage(type, text, extra = {}) {
newMessage.mes = text;
}
if (type == system_message_types.SLASH_COMMANDS) {
if (type === system_message_types.SLASH_COMMANDS) {
newMessage.mes = getSlashCommandsHelp();
}
@ -2665,7 +2708,7 @@ export function sendSystemMessage(type, text, extra = {}) {
chat.push(newMessage);
addOneMessage(newMessage);
is_send_press = false;
if (type == system_message_types.SLASH_COMMANDS) {
if (type === system_message_types.SLASH_COMMANDS) {
const browser = new SlashCommandBrowser();
const spinner = document.querySelector('#chat .last_mes .custom-slashHelp');
const parent = spinner.parentElement;
@ -7854,7 +7897,7 @@ function openAlternateGreetings() {
if (menu_type !== 'create') {
await createOrEditCharacter();
}
}
},
});
for (let index = 0; index < getArray().length; index++) {
@ -9070,6 +9113,90 @@ function doTogglePanels() {
return '';
}
/**
* Event handler to open a navbar drawer when a drawer open button is clicked.
* Handles click events on .drawer-opener elements.
* Opens the drawer associated with the clicked button according to the data-target attribute.
* @returns {void}
*/
function doDrawerOpenClick() {
const targetDrawerID = $(this).attr('data-target');
const drawer = $(`#${targetDrawerID}`);
const drawerToggle = drawer.find('.drawer-toggle');
const drawerWasOpenAlready = drawerToggle.parent().find('.drawer-content').hasClass('openDrawer');
if (drawerWasOpenAlready || drawer.hasClass('resizing')) { return; }
doNavbarIconClick.call(drawerToggle);
}
/**
* Event handler to open or close a navbar drawer when a navbar icon is clicked.
* Handles click events on .drawer-toggle elements.
* @returns {void}
*/
function doNavbarIconClick() {
var icon = $(this).find('.drawer-icon');
var drawer = $(this).parent().find('.drawer-content');
if (drawer.hasClass('resizing')) { return; }
var drawerWasOpenAlready = $(this).parent().find('.drawer-content').hasClass('openDrawer');
let targetDrawerID = $(this).parent().find('.drawer-content').attr('id');
const pinnedDrawerClicked = drawer.hasClass('pinnedOpen');
if (!drawerWasOpenAlready) { //to open the drawer
$('.openDrawer').not('.pinnedOpen').addClass('resizing').slideToggle(200, 'swing', async function () {
await delay(50); $(this).closest('.drawer-content').removeClass('resizing');
});
$('.openIcon').toggleClass('closedIcon openIcon');
$('.openDrawer').not('.pinnedOpen').toggleClass('closedDrawer openDrawer');
icon.toggleClass('openIcon closedIcon');
drawer.toggleClass('openDrawer closedDrawer');
//console.log(targetDrawerID);
if (targetDrawerID === 'right-nav-panel') {
$(this).closest('.drawer').find('.drawer-content').addClass('resizing').slideToggle({
duration: 200,
easing: 'swing',
start: function () {
jQuery(this).css('display', 'flex'); //flex needed to make charlist scroll
},
complete: async function () {
favsToHotswap();
await delay(50);
$(this).closest('.drawer-content').removeClass('resizing');
$('#rm_print_characters_block').trigger('scroll');
},
});
} else {
$(this).closest('.drawer').find('.drawer-content').addClass('resizing').slideToggle(200, 'swing', async function () {
await delay(50); $(this).closest('.drawer-content').removeClass('resizing');
});
}
// Set the height of "autoSetHeight" textareas within the drawer to their scroll height
if (!CSS.supports('field-sizing', 'content')) {
$(this).closest('.drawer').find('.drawer-content textarea.autoSetHeight').each(async function () {
await resetScrollHeight($(this));
return;
});
}
} else if (drawerWasOpenAlready) { //to close manually
icon.toggleClass('closedIcon openIcon');
if (pinnedDrawerClicked) {
$(drawer).addClass('resizing').slideToggle(200, 'swing', async function () {
await delay(50); $(this).removeClass('resizing');
});
}
else {
$('.openDrawer').not('.pinnedOpen').addClass('resizing').slideToggle(200, 'swing', async function () {
await delay(50); $(this).closest('.drawer-content').removeClass('resizing');
});
}
drawer.toggleClass('closedDrawer openDrawer');
}
}
function addDebugFunctions() {
const doBackfill = async () => {
for (const message of chat) {
@ -10654,69 +10781,8 @@ jQuery(async function () {
stopScriptExecution();
});
$('.drawer-toggle').on('click', function () {
var icon = $(this).find('.drawer-icon');
var drawer = $(this).parent().find('.drawer-content');
if (drawer.hasClass('resizing')) { return; }
var drawerWasOpenAlready = $(this).parent().find('.drawer-content').hasClass('openDrawer');
let targetDrawerID = $(this).parent().find('.drawer-content').attr('id');
const pinnedDrawerClicked = drawer.hasClass('pinnedOpen');
if (!drawerWasOpenAlready) { //to open the drawer
$('.openDrawer').not('.pinnedOpen').addClass('resizing').slideToggle(200, 'swing', async function () {
await delay(50); $(this).closest('.drawer-content').removeClass('resizing');
});
$('.openIcon').not('.drawerPinnedOpen').toggleClass('closedIcon openIcon');
$('.openDrawer').not('.pinnedOpen').toggleClass('closedDrawer openDrawer');
icon.toggleClass('openIcon closedIcon');
drawer.toggleClass('openDrawer closedDrawer');
//console.log(targetDrawerID);
if (targetDrawerID === 'right-nav-panel') {
$(this).closest('.drawer').find('.drawer-content').addClass('resizing').slideToggle({
duration: 200,
easing: 'swing',
start: function () {
jQuery(this).css('display', 'flex'); //flex needed to make charlist scroll
},
complete: async function () {
favsToHotswap();
await delay(50);
$(this).closest('.drawer-content').removeClass('resizing');
$('#rm_print_characters_block').trigger('scroll');
},
});
} else {
$(this).closest('.drawer').find('.drawer-content').addClass('resizing').slideToggle(200, 'swing', async function () {
await delay(50); $(this).closest('.drawer-content').removeClass('resizing');
});
}
// Set the height of "autoSetHeight" textareas within the drawer to their scroll height
if (!CSS.supports('field-sizing', 'content')) {
$(this).closest('.drawer').find('.drawer-content textarea.autoSetHeight').each(async function () {
await resetScrollHeight($(this));
return;
});
}
} else if (drawerWasOpenAlready) { //to close manually
icon.toggleClass('closedIcon openIcon');
if (pinnedDrawerClicked) {
$(drawer).addClass('resizing').slideToggle(200, 'swing', async function () {
await delay(50); $(this).removeClass('resizing');
});
}
else {
$('.openDrawer').not('.pinnedOpen').addClass('resizing').slideToggle(200, 'swing', async function () {
await delay(50); $(this).closest('.drawer-content').removeClass('resizing');
});
}
drawer.toggleClass('closedDrawer openDrawer');
}
});
$(document).on('click', '.drawer-opener', doDrawerOpenClick);
$('.drawer-toggle').on('click', doNavbarIconClick);
$('html').on('touchstart mousedown', function (e) {
var clickTarget = $(e.target);

View File

@ -474,7 +474,7 @@ async function processTtsQueue() {
}
if (extension_settings.tts.narrate_quoted_only) {
const special_quotes = /[“”«»]/g; // Extend this regex to include other special quotes
const special_quotes = /[“”«»「」『』""]/g; // Extend this regex to include other special quotes
text = text.replace(special_quotes, '"');
const matches = text.match(/".*?"/g); // Matches text inside double quotes, non-greedily
const partJoiner = (ttsProvider?.separator || ' ... ');

View File

@ -70,12 +70,12 @@ import {
animation_duration,
depth_prompt_role_default,
shouldAutoContinue,
this_chid,
} from '../script.js';
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, Popup, callGenericPopup } from './popup.js';
import { t } from './i18n.js';
export {
selected_group,

View File

@ -215,6 +215,7 @@ function addLanguagesToDropdown() {
}
export async function initLocales() {
moment.locale(localeFile);
langs = await fetch('/locales/lang.json').then(response => response.json());
localeData = await getLocaleData(localeFile);
applyLocale();

View File

@ -4,30 +4,56 @@
<a href="https://docs.sillytavern.app/usage/update/" target="_blank" data-i18n="Want to update?">
Want to update?
</a>
<hr>
<h3 data-i18n="How to start chatting?">How to start chatting?</h3>
<ol>
<li>
<span data-i18n="Click _space">Click </span><code><i class="fa-solid fa-plug"></i></code><span data-i18n="and select a"> and select a </span><a href="https://docs.sillytavern.app/usage/api-connections/" target="_blank" data-i18n="Chat API">Chat API</a>.</span>
<span data-i18n="Click _space">Click </span>
<button class="menu_button menu_button_icon drawer-opener inline-flex" data-target="sys-settings-button">
<i class="fa-solid fa-plug"></i>
<span data-i18n="[title]API Connections">API Connections</span>
</button>
<span data-i18n="and connect to an">and connect to an</span>
<a href="https://docs.sillytavern.app/usage/api-connections/" target="_blank">
<span class="fa-solid fa-circle-question"></span>
<span data-i18n="API">API</span></a>.
</li>
<li>
<span data-i18n="Click _space">Click </span><code><i class="fa-solid fa-address-card"></i></code><span data-i18n="and pick a character."> and pick a character.</span>
<span data-i18n="Click _space">Click </span>
<button class="menu_button menu_button_icon drawer-opener inline-flex" data-target="rightNavHolder">
<i class="fa-solid fa-address-card"></i>
<span data-i18n="[title]Character Management">Character Management</span>
</button>
<span data-i18n="and pick a character."> and pick a character.</span>
</li>
</ol>
<div>
<span data-i18n="You can browse a list of bundled characters in the">
You can browse a list of bundled characters in the
</span>
<i data-i18n="Download Extensions & Assets">
Download Extensions & Assets
</i>
<span data-i18n="menu within">
menu within
</span>
<code><i class="fa-solid fa-cubes"></i></code>
<span>.</span>
<span data-i18n="You can add more">You can add more</span>
<button class="open_characters_library menu_button menu_button_icon inline-flex">
<i class="fa-solid fa-image-portrait"></i>
<span data-i18n="Sample characters">Sample characters</span>
</button>
<span data-i18n="or">or</span>
<button class="external_import_button menu_button menu_button_icon inline-flex">
<i class="fa-solid fa-cloud-arrow-down"></i>
<span data-i18n="Import Characters">Import characters</span>
</button>
<span data-i18n="from other websites">from other websites.</span>
</div>
<hr>
<div>
<span data-i18n="Go to the">Go to the</span>
<i data-i18n="Download Extensions & Assets">Download Extensions & Assets</i>
<span data-i18n="menu within">menu within</span>
<button class="menu_button menu_button_icon drawer-opener inline-flex" data-target="extensions-settings-button">
<i class="fa-solid fa-cubes"></i>
<span data-i18n="[title]Extensions">Extensions</span>
</button>
<span data-i18n="to install additional features.">to install additional features.</span>
</div>
<h3 data-i18n="Confused or lost?">Confused or lost?</h3>
<ul>
<li>
@ -43,7 +69,6 @@
</li>
</ul>
<hr>
<h3 data-i18n="Still have questions?">Still have questions?</h3>
<ul>
<li>
@ -62,4 +87,3 @@
</a>
</li>
</ul>

View File

@ -314,6 +314,12 @@ input[type='checkbox']:focus-visible {
display: inline-block;
}
.mes_text ol,
.mes_text ul {
margin-top: 5px;
margin-bottom: 5px;
}
.mes_text br,
.mes_bias br {
content: ' ';
@ -1208,8 +1214,9 @@ textarea.autoSetHeight {
}
input,
select {
font-family: var(--mainFontFamily);
select,
button {
font-family: var(--mainFontFamily), sans-serif;
font-size: var(--mainFontSize);
color: var(--SmartThemeBodyColor);
}