From 02b0000117e6b9136ed9014d9eb2c1655ccb7761 Mon Sep 17 00:00:00 2001
From: ceruleandeep
Date: Sat, 5 Oct 2024 17:26:28 +1000
Subject: [PATCH 1/3] Add clickable buttons in Welcome chat message.
Add bool `uses_system_ui` on system messages to override sanitizer for buttons when set
Modify uponSanitizeAttribute DOMPurify hook to allow unmangled class names on attributes in some cases
Add event listener for .drawer-opener to open a navbar drawer
---
public/script.js | 192 ++++++++++++++++----------
public/scripts/templates/welcome.html | 124 ++++++++++-------
public/style.css | 5 +-
3 files changed, 200 insertions(+), 121 deletions(-)
diff --git a/public/script.js b/public/script.js
index 68cd86ed3..d82373400 100644
--- a/public/script.js
+++ b/public/script.js
@@ -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,7 +657,8 @@ async function getSystemMessages() {
force_avatar: system_avatar,
is_user: false,
is_system: true,
- mes: await renderTemplateAsync('welcome', { displayVersion }),
+ uses_system_ui: true,
+ mes: await renderTemplateAsync('welcome', { displayVersion } ),
},
group: {
name: systemUserName,
@@ -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 '';
}
@@ -2029,7 +2038,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 +2243,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 +2283,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 +2298,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 +2355,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 +2676,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 +2690,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 +7879,7 @@ function openAlternateGreetings() {
if (menu_type !== 'create') {
await createOrEditCharacter();
}
- }
+ },
});
for (let index = 0; index < getArray().length; index++) {
@@ -9070,6 +9095,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) {
@@ -10659,69 +10768,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);
diff --git a/public/scripts/templates/welcome.html b/public/scripts/templates/welcome.html
index 82eb07097..df887c3ad 100644
--- a/public/scripts/templates/welcome.html
+++ b/public/scripts/templates/welcome.html
@@ -1,65 +1,95 @@
- {{displayVersion}}
+ {{displayVersion}}
- Want to update?
+ Want to update?
How to start chatting?
- -
- Click
and select a Chat API.
-
- -
- Click
and pick a character.
-
+ -
+ Click
+
+ and select a
+
+
+ Chat API.
+
+ -
+ Click
+
+ and pick a character.
+
-
-
- You can browse a list of bundled characters in the
-
-
- Download Extensions & Assets
-
-
- menu within
-
-
- .
-
+
+
+ You can add more
+
+ or
+
+ from other websites.
+
+ Go to the
+
+ Download Extensions & Assets
+ menu within
+
+
+
+ to install additional features.
+
+
Confused or lost?
Still have questions?
diff --git a/public/style.css b/public/style.css
index 01a49ab9f..6ee4f6d60 100644
--- a/public/style.css
+++ b/public/style.css
@@ -1208,8 +1208,9 @@ textarea.autoSetHeight {
}
input,
-select {
- font-family: var(--mainFontFamily);
+select,
+button {
+ font-family: var(--mainFontFamily), sans-serif;
font-size: var(--mainFontSize);
color: var(--SmartThemeBodyColor);
}
From 60fef3ee99705be644c25e578b666344af8af81d Mon Sep 17 00:00:00 2001
From: Cohee <18619528+Cohee1207@users.noreply.github.com>
Date: Sat, 5 Oct 2024 19:45:41 +0300
Subject: [PATCH 2/3] Restyle welcome
---
public/script.js | 4 +-
public/scripts/templates/welcome.html | 149 +++++++++++++-------------
public/style.css | 6 ++
3 files changed, 81 insertions(+), 78 deletions(-)
diff --git a/public/script.js b/public/script.js
index d82373400..7651cf3fd 100644
--- a/public/script.js
+++ b/public/script.js
@@ -658,7 +658,7 @@ async function getSystemMessages() {
is_user: false,
is_system: true,
uses_system_ui: true,
- mes: await renderTemplateAsync('welcome', { displayVersion } ),
+ mes: await renderTemplateAsync('welcome', { displayVersion }),
},
group: {
name: systemUserName,
@@ -9106,7 +9106,7 @@ function doDrawerOpenClick() {
const drawer = $(`#${targetDrawerID}`);
const drawerToggle = drawer.find('.drawer-toggle');
const drawerWasOpenAlready = drawerToggle.parent().find('.drawer-content').hasClass('openDrawer');
- if (drawerWasOpenAlready || drawer.hasClass('resizing') ) { return; }
+ if (drawerWasOpenAlready || drawer.hasClass('resizing')) { return; }
doNavbarIconClick.call(drawerToggle);
}
diff --git a/public/scripts/templates/welcome.html b/public/scripts/templates/welcome.html
index df887c3ad..eaf49b986 100644
--- a/public/scripts/templates/welcome.html
+++ b/public/scripts/templates/welcome.html
@@ -1,95 +1,92 @@
- {{displayVersion}}
+ {{displayVersion}}
- Want to update?
+ Want to update?
-
How to start chatting?
- -
- Click
-
- and select a
-
-
- Chat API.
-
- -
- Click
-
- and pick a character.
-
+ -
+ Click
+
+ and connect to an
+
+
+ API.
+
+ -
+ Click
+
+ and pick a character.
+
-
- You can add more
-
- or
-
- from other websites.
+
+ You can add more
+
+ or
+
+ from other websites.
+
+
+ Go to the
- Go to the
+ Download Extensions & Assets
+ menu within
- Download Extensions & Assets
- menu within
+
-
+ to install additional features.
+
- to install additional features.
-
-
-
Confused or lost?
-
Still have questions?
-
diff --git a/public/style.css b/public/style.css
index 6ee4f6d60..8bb8d5809 100644
--- a/public/style.css
+++ b/public/style.css
@@ -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: ' ';
From 24300642dd6cdf678b9da03bf2b88874f0d00ac2 Mon Sep 17 00:00:00 2001
From: Cohee <18619528+Cohee1207@users.noreply.github.com>
Date: Sat, 5 Oct 2024 19:48:20 +0300
Subject: [PATCH 3/3] Cleaner HTML Diff
---
public/scripts/templates/welcome.html | 7 ++-----
1 file changed, 2 insertions(+), 5 deletions(-)
diff --git a/public/scripts/templates/welcome.html b/public/scripts/templates/welcome.html
index eaf49b986..202609012 100644
--- a/public/scripts/templates/welcome.html
+++ b/public/scripts/templates/welcome.html
@@ -57,9 +57,7 @@
Confused or lost?
-
- - click these
- icons!
+ - click these icons!
-
Enter
/?
in the chat bar
@@ -84,8 +82,7 @@
-
-
+
Contact the developers