UI performance fixes (#3207) * Optimize visibility checks for burger and wand menus * Optimize message actions visibility toggle * Run drawer toggle in animation frame * Replace jQuery slideToggle with a 3rd-party lib * Refactor export button functionality to manage popup state with a boolean flag * Do not close the pinned drawer on unpin * Revert "Do not close the pinned drawer on unpin" This reverts commit e3b34e9a586db853dd84809f4187d5b29cb9ac36. * Refactor slideToggle options * ease-in-out * Don't skip frame on drawer toggle

94de9411b69b02221e6bdec46d4b6af1a1b0b6d4

Cohee <18619528+Cohee1207@users.noreply.github.com>

Signed
7 files changed, +140 -86Showing whitespace changes
package-lock.json+7 -0
@@ -63,6 +63,7 @@
6363 "showdown": "^2.1.0",
6464 "sillytavern-transformers": "2.14.6",
6565 "simple-git": "^3.19.1",
66+ "slidetoggle": "^4.0.0",
6667 "tiktoken": "^1.0.16",
6768 "vectra": "^0.2.2",
6869 "wavefile": "^11.0.0",
@@ -6531,6 +6532,12 @@
65316532 "integrity": "sha512-VZBmZP8WU3sMOZm1bdgTadsQbcscK0UM8oKxKVBs4XAhUo2Xxzm/OFMGBkPusxw9xL3Uy8LrzEqGqJhclsr0yA==",
65326533 "license": "MIT"
65336534 },
6535+ "node_modules/slidetoggle": {
6536+ "version": "4.0.0",
6537+ "resolved": "https://registry.npmjs.org/slidetoggle/-/slidetoggle-4.0.0.tgz",
6538+ "integrity": "sha512-6qvrOS1dnDFEr41UEEFFRQE8nswaAFIYZAHer6dVlznRIjHyCISjNJoxIn5U5QlAbZfBBxTELQk4jS7miHto1A==",
6539+ "license": "MIT"
6540+ },
65346541 "node_modules/smart-buffer": {
65356542 "version": "4.2.0",
65366543 "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
package.json+1 -0
@@ -53,6 +53,7 @@
5353 "showdown": "^2.1.0",
5454 "sillytavern-transformers": "2.14.6",
5555 "simple-git": "^3.19.1",
56+ "slidetoggle": "^4.0.0",
5657 "tiktoken": "^1.0.16",
5758 "vectra": "^0.2.2",
5859 "wavefile": "^11.0.0",
public/lib.js+3 -0
@@ -19,6 +19,7 @@ import seedrandom from 'seedrandom';
1919import * as Popper from '@popperjs/core';
2020import droll from 'droll';
2121import morphdom from 'morphdom';
22+import { toggle as slideToggle } from 'slidetoggle';
2223
2324/**
2425 * Expose the libraries to the 'window' object.
@@ -94,6 +95,7 @@ export default {
9495 Popper,
9596 droll,
9697 morphdom,
98+ slideToggle,
9799};
98100
99101export {
@@ -115,4 +117,5 @@ export {
115117 Popper,
116118 droll,
117119 morphdom,
120+ slideToggle,
118121};
public/script.js+111 -74
@@ -10,6 +10,7 @@ import {
1010 SVGInject,
1111 Popper,
1212 initLibraryShims,
13+ slideToggle,
1314 default as libs,
1415} from './lib.js';
1516
@@ -549,6 +550,7 @@ let optionsPopper = Popper.createPopper(document.getElementById('options_button'
549550let exportPopper = Popper.createPopper(document.getElementById('export_button'), document.getElementById('export_format_popup'), {
550551 placement: 'left',
551552});
553+let isExportPopupOpen = false;
552554
553555// Saved here for performance reasons
554556const messageTemplate = $('#message_template .mes');
@@ -895,6 +897,13 @@ export function getRequestHeaders() {
895897 };
896898}
897899
900+export function getSlideToggleOptions() {
901+ return {
902+ miliseconds: animation_duration * 1.5,
903+ transitionFunction: animation_duration > 0 ? 'ease-in-out' : 'step-start',
904+ };
905+}
906+
898907$.ajaxPrefilter((options, originalOptions, xhr) => {
899908 xhr.setRequestHeader('X-CSRF-Token', token);
900909});
@@ -9209,40 +9218,48 @@ function doDrawerOpenClick() {
92099218 * @returns {void}
92109219 */
92119220function doNavbarIconClick() {
92129221 varconst icon = $(this).find('.drawer-icon');
92139222 varconst drawer = $(this).parent().find('.drawer-content');
92149223 if (drawer.hasClass('resizing')) { return; }
92159224 varconst drawerWasOpenAlready = $(this).parent().find('.drawer-content').hasClass('openDrawer');
92169225 letconst targetDrawerID = $(this).parent().find('.drawer-content').attr('id');
92179226 const pinnedDrawerClicked = drawer.hasClass('pinnedOpen');
92189227
92199228 if (!drawerWasOpenAlready) { //to open the drawer
92209229 $('.openDrawer').not('.pinnedOpen').addClass('resizing').slideToggleeach(200, 'swing'(_, async function (el) => {
9221- await delay(50); $(this).closest('.drawer-content').removeClass('resizing');
9230+ slideToggle(el, {
9231+ ...getSlideToggleOptions(),
9232+ onAnimationEnd: function (el) {
9233+ el.closest('.drawer-content').classList.remove('resizing');
9234+ },
9235+ });
92229236 });
92239237 $('.openIcon').not('.drawerPinnedOpen').toggleClass('closedIcon openIcon');
92249238 $('.openDrawer').not('.pinnedOpen').toggleClass('closedDrawer openDrawer');
92259239 icon.toggleClass('openIcon closedIcon');
92269240 drawer.toggleClass('openDrawer closedDrawer');
92279241
92289242 //console.log(targetDrawerID);
92299243 if (targetDrawerID === 'right-nav-panel') {
92309244 $(this).closest('.drawer').find('.drawer-content').addClass('resizing').slideToggleeach((_, el) => {
9231- duration: 200,
9245+ slideToggle(el, {
9232- easing: 'swing',
9246+ ...getSlideToggleOptions(),
9233- start: function () {
9247+ elementDisplayStyle: 'flex',
9234- jQuery(this).css('display', 'flex'); //flex needed to make charlist scroll
9248+ onAnimationEnd: function (el) {
9235- },
9249+ el.closest('.drawer-content').classList.remove('resizing');
9236- complete: async function () {
92379250 favsToHotswap();
9238- await delay(50);
9239- $(this).closest('.drawer-content').removeClass('resizing');
92409251 $('#rm_print_characters_block').trigger('scroll');
92419252 },
92429253 });
9254+ });
92439255 } else {
92449256 $(this).closest('.drawer').find('.drawer-content').addClass('resizing').slideToggleeach(200, 'swing'(_, async function (el) => {
9245- await delay(50); $(this).closest('.drawer-content').removeClass('resizing');
9257+ slideToggle(el, {
9258+ ...getSlideToggleOptions(),
9259+ onAnimationEnd: function (el) {
9260+ el.closest('.drawer-content').classList.remove('resizing');
9261+ },
9262+ });
92469263 });
92479264 }
92489265
@@ -9258,13 +9275,23 @@ function doNavbarIconClick() {
92589275 icon.toggleClass('closedIcon openIcon');
92599276
92609277 if (pinnedDrawerClicked) {
92619278 $(drawer).addClass('resizing').slideToggleeach(200, 'swing'(_, async function (el) => {
9262- await delay(50); $(this).removeClass('resizing');
9279+ slideToggle(el, {
9280+ ...getSlideToggleOptions(),
9281+ onAnimationEnd: function (el) {
9282+ el.classList.remove('resizing');
9283+ },
9284+ });
92639285 });
92649286 }
92659287 else {
92669288 $('.openDrawer').not('.pinnedOpen').addClass('resizing').slideToggleeach(200, 'swing'(_, async function (el) => {
9267- await delay(50); $(this).closest('.drawer-content').removeClass('resizing');
9289+ slideToggle(el, {
9290+ ...getSlideToggleOptions(),
9291+ onAnimationEnd: function (el) {
9292+ el.closest('.drawer-content').classList.remove('resizing');
9293+ },
9294+ });
92689295 });
92699296 }
92709297
@@ -10086,20 +10113,21 @@ jQuery(async function () {
1008610113 await getStatusNovel();
1008710114 });
1008810115
1008910116 varconst button = $('#options_button');
1009010117 varconst menu = $('#options');
10118+ let isOptionsMenuVisible = false;
1009110119
1009210120 function showMenu() {
1009310121 showBookmarksButtons();
10094- // menu.stop()
1009510122 menu.fadeIn(animation_duration);
1009610123 optionsPopper.update();
10124+ isOptionsMenuVisible = true;
1009710125 }
1009810126
1009910127 function hideMenu() {
10100- // menu.stop();
1010110128 menu.fadeOut(animation_duration);
1010210129 optionsPopper.update();
10130+ isOptionsMenuVisible = false;
1010310131 }
1010410132
1010510133 function isMouseOverButtonOrMenu() {
@@ -10107,26 +10135,15 @@ jQuery(async function () {
1010710135 }
1010810136
1010910137 button.on('click', function () {
1011010138 if (menu.is(':visible')isOptionsMenuVisible) {
1011110139 hideMenu();
1011210140 } else {
1011310141 showMenu();
1011410142 }
1011510143 });
10116- button.on('blur', function () {
10117- //delay to prevent menu hiding when mouse leaves button into menu
10118- setTimeout(() => {
10119- if (!isMouseOverButtonOrMenu()) { hideMenu(); }
10120- }, 100);
10121- });
10122- menu.on('blur', function () {
10123- //delay to prevent menu hide when mouseleaves menu into button
10124- setTimeout(() => {
10125- if (!isMouseOverButtonOrMenu()) { hideMenu(); }
10126- }, 100);
10127- });
1012810144 $(document).on('click', function () {
10129- if (!isMouseOverButtonOrMenu() && menu.is(':visible')) { hideMenu(); }
10145+ if (!isOptionsMenuVisible) return;
10146+ if (!isMouseOverButtonOrMenu()) { hideMenu(); }
1013010147 });
1013110148
1013210149 /* $('#set_chat_scenario').on('click', setScenarioOverride); */
@@ -10522,22 +10539,28 @@ jQuery(async function () {
1052210539 });
1052310540
1052410541 $(document).on('click', '.extraMesButtonsHint', function (e) {
1052510542 const elmnt$hint = $(e.target);
10526- $(elmnt).transition({
10543+ const $buttons = $hint.siblings('.extraMesButtons');
10544+
10545+ $hint.transition({
1052710546 opacity: 0,
1052810547 duration: animation_duration,
1052910548 easing: 'ease-in-out'animation_easing,
10530- });
10549+ complete: function () {
10531- setTimeout(function () {
10550+ $hint.hide();
10532- $(elmnt).hide();
10551+ $buttons
10533- $(elmnt).siblings('.extraMesButtons').css('opcacity', '0');
10552+ .addClass('visible')
10534- $(elmnt).siblings('.extraMesButtons').css('display', 'flex');
10553+ .css({
10535- $(elmnt).siblings('.extraMesButtons').transition({
10554+ opacity: 0,
10555+ display: 'flex',
10556+ })
10557+ .transition({
1053610558 opacity: 1,
1053710559 duration: animation_duration,
10538- easing: 'ease-in-out',
10560+ easing: animation_easing,
10561+ });
10562+ },
1053910563 });
10540- }, animation_duration);
1054110564 });
1054210565
1054310566 $(document).on('click', function (e) {
@@ -10548,19 +10571,32 @@ jQuery(async function () {
1054810571
1054910572 // Check if the click was outside the relevant elements
1055010573 if (!$(e.target).closest('.extraMesButtons, .extraMesButtonsHint').length) {
10574+ const $visibleButtons = $('.extraMesButtons.visible');
10575+
10576+ if (!$visibleButtons.length) {
10577+ return;
10578+ }
10579+
10580+ const $hiddenHints = $('.extraMesButtonsHint:hidden');
10581+
1055110582 // Transition out the .extraMesButtons first
1055210583 $('.extraMesButtons:visible')visibleButtons.transition({
1055310584 opacity: 0,
1055410585 duration: animation_duration,
1055510586 easing: 'ease-in-out'animation_easing,
1055610587 complete: function () {
1055710588 $(this).hide(); // Hide the .extraMesButtons after the transition
10589+ $(this)
10590+ .hide()
10591+ .removeClass('visible');
1055810592
1055910593 // Transition the .extraMesButtonsHint back in
10560- $('.extraMesButtonsHint:not(:visible)').show().transition({
10594+ $hiddenHints
10561- opacity: .3,
10595+ .show()
10596+ .transition({
10597+ opacity: 0.3,
1056210598 duration: animation_duration,
10563- easing: 'ease-in-out',
10599+ easing: animation_easing,
1056410600 complete: function () {
1056510601 $(this).css('opacity', '');
1056610602 },
@@ -10748,8 +10784,9 @@ jQuery(async function () {
1074810784 }
1074910785 });
1075010786
1075110787 $('#export_button').on('click', function (e) {
10752- $('#export_format_popup').toggle();
10788+ isExportPopupOpen = !isExportPopupOpen;
10789+ $('#export_format_popup').toggle(isExportPopupOpen);
1075310790 exportPopper.update();
1075410791 });
1075510792
@@ -10760,6 +10797,10 @@ jQuery(async function () {
1076010797 return;
1076110798 }
1076210799
10800+ $('#export_format_popup').hide();
10801+ isExportPopupOpen = false;
10802+ exportPopper.update();
10803+
1076310804 // Save before exporting
1076410805 await createOrEditCharacter();
1076510806 const body = { format, avatar_url: characters[this_chid].avatar };
@@ -10781,9 +10822,6 @@ jQuery(async function () {
1078110822 URL.revokeObjectURL(a.href);
1078210823 document.body.removeChild(a);
1078310824 }
10784-
10785-
10786- $('#export_format_popup').hide();
1078710825 });
1078810826 //**************************CHAT IMPORT EXPORT*************************//
1078910827 $('#chat_import_button').click(function () {
@@ -10855,15 +10893,18 @@ jQuery(async function () {
1085510893 });
1085610894
1085710895 $(document).on('click', '.drawer-opener', doDrawerOpenClick);
10896+
1085810897 $('.drawer-toggle').on('click', doNavbarIconClick);
1085910898
1086010899 $('html').on('touchstart mousedown', function (e) {
1086110900 var clickTarget = $(e.target);
1086210901
10863- if ($('#export_format_popup').is(':visible')
10902+ if (isExportPopupOpen
1086410903 && clickTarget.closest('#export_button').length == 0
1086510904 && clickTarget.closest('#export_format_popup').length == 0) {
1086610905 $('#export_format_popup').hide();
10906+ isExportPopupOpen = false;
10907+ exportPopper.update();
1086710908 }
1086810909
1086910910 const forbiddenTargets = [
@@ -10888,12 +10929,16 @@ jQuery(async function () {
1088810929 if ($('.openDrawer').length !== 0) {
1088910930 if (targetParentHasOpenDrawer === 0) {
1089010931 //console.log($('.openDrawer').not('.pinnedOpen').length);
1089110932 $('.openDrawer').not('.pinnedOpen').addClass('resizing').slideToggleeach(200, 'swing'(_, function (el) => {
10892- $(this).closest('.drawer-content').removeClass('resizing');
10933+ slideToggle(el, {
10934+ ...getSlideToggleOptions(),
10935+ onAnimationEnd: (el) => {
10936+ el.closest('.drawer-content').classList.remove('resizing');
10937+ },
10938+ });
1089310939 });
1089410940 $('.openIcon').not('.drawerPinnedOpen').toggleClass('closedIcon openIcon');
1089510941 $('.openDrawer').not('.pinnedOpen').toggleClass('closedDrawer openDrawer');
10896-
1089710942 }
1089810943 }
1089910944 }
@@ -11059,14 +11104,6 @@ jQuery(async function () {
1105911104 case 'renameCharButton':
1106011105 renameCharacter();
1106111106 break;
11062- /*case 'dupe_button':
11063- DupeChar();
11064- break;
11065- case 'export_button':
11066- $('#export_format_popup').toggle();
11067- exportPopper.update();
11068- break;
11069- */
1107011107 case 'import_character_info':
1107111108 await importEmbeddedWorldInfo();
1107211109 saveCharacterDebounced();
public/scripts/RossAscends-mods.js+8 -7
@@ -1,4 +1,4 @@
11import { DOMPurify, Bowser, slideToggle } from '../lib.js';
22
33import {
44 characters,
@@ -19,6 +19,7 @@ import {
1919 menu_type,
2020 substituteParams,
2121 sendTextareaMessage,
22+ getSlideToggleOptions,
2223} from '../script.js';
2324
2425import {
@@ -748,8 +749,8 @@ export function initRossMods() {
748749 $(RightNavDrawerIcon).removeClass('drawerPinnedOpen');
749750
750751 if ($(RightNavPanel).hasClass('openDrawer') && $('.openDrawer').length > 1) {
751752 $(RightNavPanel).slideToggle(200RightNavPanel, 'swing'getSlideToggleOptions());
752753 $(RightNavDrawerIcon).toggleClass('openIcon closedIcon openIcon');
753754 $(RightNavPanel).toggleClass('openDrawer closedDrawer');
754755 }
755756 }
@@ -766,8 +767,8 @@ export function initRossMods() {
766767 $(LeftNavDrawerIcon).removeClass('drawerPinnedOpen');
767768
768769 if ($(LeftNavPanel).hasClass('openDrawer') && $('.openDrawer').length > 1) {
769770 $(LeftNavPanel).slideToggle(200LeftNavPanel, 'swing'getSlideToggleOptions());
770771 $(LeftNavDrawerIcon).toggleClass('openIcon closedIcon openIcon');
771772 $(LeftNavPanel).toggleClass('openDrawer closedDrawer');
772773 }
773774 }
@@ -786,8 +787,8 @@ export function initRossMods() {
786787
787788 if ($(WorldInfo).hasClass('openDrawer') && $('.openDrawer').length > 1) {
788789 console.debug('closing WI after lock removal');
789790 $(WorldInfo).slideToggle(200WorldInfo, 'swing'getSlideToggleOptions());
790791 $(WIDrawerIcon).toggleClass('openIcon closedIcon openIcon');
791792 $(WorldInfo).toggleClass('openDrawer closedDrawer');
792793 }
793794 }
public/scripts/extensions.js+8 -4
@@ -417,26 +417,30 @@ async function addExtensionsButtonAndMenu() {
417417
418418 const button = $('#extensionsMenuButton');
419419 const dropdown = $('#extensionsMenu');
420- //dropdown.hide();
420+ let isDropdownVisible = false;
421421
422422 let popper = Popper.createPopper(button.get(0), dropdown.get(0), {
423423 placement: 'top-start',
424424 });
425425
426426 $(button).on('click', function () {
427427 if (dropdown.is(':visible')isDropdownVisible) {
428428 dropdown.fadeOut(animation_duration);
429+ isDropdownVisible = false;
429430 } else {
430431 dropdown.fadeIn(animation_duration);
432+ isDropdownVisible = true;
431433 }
432434 popper.update();
433435 });
434436
435437 $('html').on('click', function (e) {
438+ if (!isDropdownVisible) return;
436439 const clickTarget = $(e.target);
437440 const noCloseTargets = ['#sd_gen', '#extensionsMenuButton', '#roll_dice'];
438441 if (dropdown.is(':visible') && !noCloseTargets.some(id => clickTarget.closest(id).length > 0)) {
439442 $(dropdown).fadeOut(animation_duration);
443+ isDropdownVisible = false;
440444 }
441445 });
442446}
public/style.css+2 -1
@@ -2874,6 +2874,7 @@ input[type=search]:focus::-webkit-search-cancel-button {
28742874
28752875.mes_block .ch_name {
28762876 max-width: 100%;
2877+ min-height: 22px;
28772878}
28782879
28792880/*applies to both groups and solos chars in the char list*/
@@ -4043,7 +4044,7 @@ input[type="range"]::-webkit-slider-thumb {
40434044.mes_button,
40444045.extraMesButtons>div {
40454046 cursor: pointer;
40464047 transition: opacity 0.3s2s ease-in-out;
40474048 filter: drop-shadow(0px 0px 2px black);
40484049 opacity: 0.3;
40494050 padding: 1px 3px;