Merge branch 'SillyTavern:staging' into staging

This commit is contained in:
Tony Ribeiro 2023-10-10 01:51:30 +02:00 committed by GitHub
commit 47d4561bf8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 270 additions and 116 deletions

4
package-lock.json generated
View File

@ -1,12 +1,12 @@
{ {
"name": "sillytavern", "name": "sillytavern",
"version": "1.10.5", "version": "1.10.6",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "sillytavern", "name": "sillytavern",
"version": "1.10.5", "version": "1.10.6",
"hasInstallScript": true, "hasInstallScript": true,
"license": "AGPL-3.0", "license": "AGPL-3.0",
"dependencies": { "dependencies": {

View File

@ -47,7 +47,7 @@
"type": "git", "type": "git",
"url": "https://github.com/SillyTavern/SillyTavern.git" "url": "https://github.com/SillyTavern/SillyTavern.git"
}, },
"version": "1.10.5", "version": "1.10.6",
"scripts": { "scripts": {
"start": "node server.js", "start": "node server.js",
"start-multi": "node server.js --disableCsrf", "start-multi": "node server.js --disableCsrf",

View File

@ -103,39 +103,39 @@ input.extension_missing[type="checkbox"] {
} }
/** LEFT COLUMN **/ /** LEFT COLUMN **/
#extensions_settings>.expression_settings { #extensions_settings>#assets_ui {
order: 1; order: 1;
} }
#extensions_settings>.background_settings { #extensions_settings>.expression_settings {
order: 2; order: 2;
} }
#extensions_settings>.sd_settings { #extensions_settings>.background_settings {
order: 3; order: 3;
} }
#extensions_settings>#tts_settings { #extensions_settings>.sd_settings {
order: 4; order: 4;
} }
#extensions_settings>#rvc_settings { #extensions_settings>#tts_settings {
order: 5; order: 5;
} }
#extensions_settings>.objective-settings { #extensions_settings>#rvc_settings {
order: 6; order: 6;
} }
#extensions_settings>#speech_recognition_settings { #extensions_settings>.objective-settings {
order: 7; order: 7;
} }
#extensions_settings>#audio_settings { #extensions_settings>#speech_recognition_settings {
order: 8; order: 8;
} }
#extensions_settings>#assets_ui { #extensions_settings>#audio_settings {
order: 9; order: 9;
} }

View File

@ -188,7 +188,8 @@
#showRawPrompt, #showRawPrompt,
#copyPromptToClipboard, #copyPromptToClipboard,
#groupCurrentMemberPopoutButton { #groupCurrentMemberPopoutButton,
#summaryExtensionPopoutButton {
display: none; display: none;
} }

View File

@ -329,7 +329,8 @@ body.movingUI #expression-holder,
body.movingUI .zoomed_avatar, body.movingUI .zoomed_avatar,
body.movingUI .draggable, body.movingUI .draggable,
body.movingUI #floatingPrompt, body.movingUI #floatingPrompt,
body.movingUI #groupMemberListPopout { body.movingUI #groupMemberListPopout,
body.movingUI #summaryExtensionPopout {
resize: both; resize: both;
} }

View File

@ -339,6 +339,7 @@ export let isChatSaving = false;
let chat_create_date = 0; let chat_create_date = 0;
let firstRun = false; let firstRun = false;
let settingsReady = false; let settingsReady = false;
let currentVersion = "0.0.0";
const default_ch_mes = "Hello"; const default_ch_mes = "Hello";
let count_view_mes = 0; let count_view_mes = 0;
@ -537,6 +538,7 @@ async function getClientVersion() {
const data = await response.json(); const data = await response.json();
CLIENT_VERSION = data.agent; CLIENT_VERSION = data.agent;
let displayVersion = `SillyTavern ${data.pkgVersion}`; let displayVersion = `SillyTavern ${data.pkgVersion}`;
currentVersion = data.pkgVersion;
if (data.gitRevision && data.gitBranch) { if (data.gitRevision && data.gitBranch) {
displayVersion += ` '${data.gitBranch}' (${data.gitRevision})`; displayVersion += ` '${data.gitBranch}' (${data.gitRevision})`;
@ -4554,7 +4556,6 @@ async function getChatResult() {
if (chat.length === 1) { if (chat.length === 1) {
const chat_id = (chat.length - 1); const chat_id = (chat.length - 1);
await eventSource.emit(event_types.MESSAGE_RECEIVED, chat_id); await eventSource.emit(event_types.MESSAGE_RECEIVED, chat_id);
addOneMessage(chat[chat_id]);
await eventSource.emit(event_types.CHARACTER_MESSAGE_RENDERED, chat_id); await eventSource.emit(event_types.CHARACTER_MESSAGE_RENDERED, chat_id);
} }
} }
@ -5046,7 +5047,8 @@ async function getSettings(type) {
selected_button = settings.selected_button; selected_button = settings.selected_button;
if (data.enable_extensions) { if (data.enable_extensions) {
await loadExtensionSettings(settings); const isVersionChanged = settings.currentVersion !== currentVersion;
await loadExtensionSettings(settings, isVersionChanged);
eventSource.emit(event_types.EXTENSION_SETTINGS_LOADED); eventSource.emit(event_types.EXTENSION_SETTINGS_LOADED);
} }
} }
@ -5072,6 +5074,7 @@ async function saveSettings(type) {
url: "/savesettings", url: "/savesettings",
data: JSON.stringify({ data: JSON.stringify({
firstRun: firstRun, firstRun: firstRun,
currentVersion: currentVersion,
username: name1, username: name1,
active_character: active_character, active_character: active_character,
active_group: active_group, active_group: active_group,

View File

@ -609,6 +609,15 @@ async function showExtensionsDetails() {
*/ */
async function onUpdateClick() { async function onUpdateClick() {
const extensionName = $(this).data('name'); const extensionName = $(this).data('name');
await updateExtension(extensionName, false);
}
/**
* Updates a third-party extension via the API.
* @param {string} extensionName Extension folder name
* @param {boolean} quiet If true, don't show a success message
*/
async function updateExtension(extensionName, quiet) {
try { try {
const response = await fetch('/api/extensions/update', { const response = await fetch('/api/extensions/update', {
method: 'POST', method: 'POST',
@ -618,15 +627,20 @@ async function onUpdateClick() {
const data = await response.json(); const data = await response.json();
if (data.isUpToDate) { if (data.isUpToDate) {
if (!quiet) {
toastr.success('Extension is already up to date'); toastr.success('Extension is already up to date');
} else {
toastr.success(`Extension updated to ${data.shortCommitHash}`);
} }
} else {
toastr.success(`Extension ${extensionName} updated to ${data.shortCommitHash}`);
}
if (!quiet) {
showExtensionsDetails(); showExtensionsDetails();
}
} catch (error) { } catch (error) {
console.error('Error:', error); console.error('Error:', error);
} }
}; }
/** /**
* Handles the click event for the delete button of an extension. * Handles the click event for the delete button of an extension.
@ -706,11 +720,16 @@ export async function installExtension(url) {
const response = await request.json(); const response = await request.json();
toastr.success(`Extension "${response.display_name}" by ${response.author} (version ${response.version}) has been imported successfully!`, 'Extension import successful'); toastr.success(`Extension "${response.display_name}" by ${response.author} (version ${response.version}) has been imported successfully!`, 'Extension import successful');
console.debug(`Extension "${response.display_name}" has been imported successfully at ${response.extensionPath}`); console.debug(`Extension "${response.display_name}" has been imported successfully at ${response.extensionPath}`);
await loadExtensionSettings({}); await loadExtensionSettings({}, false);
eventSource.emit(event_types.EXTENSION_SETTINGS_LOADED); eventSource.emit(event_types.EXTENSION_SETTINGS_LOADED);
} }
async function loadExtensionSettings(settings) { /**
* Loads extension settings from the app settings.
* @param {object} settings App Settings
* @param {boolean} versionChanged Is this a version change?
*/
async function loadExtensionSettings(settings, versionChanged) {
if (settings.extension_settings) { if (settings.extension_settings) {
Object.assign(extension_settings, settings.extension_settings); Object.assign(extension_settings, settings.extension_settings);
} }
@ -723,10 +742,25 @@ async function loadExtensionSettings(settings) {
eventSource.emit(event_types.EXTENSIONS_FIRST_LOAD); eventSource.emit(event_types.EXTENSIONS_FIRST_LOAD);
extensionNames = await discoverExtensions(); extensionNames = await discoverExtensions();
manifests = await getManifests(extensionNames) manifests = await getManifests(extensionNames)
if (versionChanged) {
await autoUpdateExtensions();
}
await activateExtensions(); await activateExtensions();
if (extension_settings.autoConnect && extension_settings.apiUrl) { if (extension_settings.autoConnect && extension_settings.apiUrl) {
connectToApi(extension_settings.apiUrl); connectToApi(extension_settings.apiUrl);
} }
}
async function autoUpdateExtensions() {
for (const [id, manifest] of Object.entries(manifests)) {
if (manifest.auto_update && id.startsWith('third-party')) {
console.debug(`Auto-updating 3rd-party extension: ${manifest.display_name} (${id})`);
await updateExtension(id.replace('third-party', ''), true);
}
}
} }
async function runGenerationInterceptors(chat, contextSize) { async function runGenerationInterceptors(chat, contextSize) {

View File

@ -47,8 +47,10 @@ function downloadAssetsList(url) {
} }
console.debug(DEBUG_PREFIX, "Updated available assets to", availableAssets); console.debug(DEBUG_PREFIX, "Updated available assets to", availableAssets);
// First extensions, then everything else
const assetTypes = Object.keys(availableAssets).sort((a, b) => (a === 'extension') ? -1 : (b === 'extension') ? 1 : 0);
for (const assetType in availableAssets) { for (const assetType of assetTypes) {
let assetTypeMenu = $('<div />', { id: "assets_audio_ambient_div", class: "assets-list-div" }); let assetTypeMenu = $('<div />', { id: "assets_audio_ambient_div", class: "assets-list-div" });
assetTypeMenu.append(`<h3>${assetType}</h3>`) assetTypeMenu.append(`<h3>${assetType}</h3>`)
for (const i in availableAssets[assetType]) { for (const i in availableAssets[assetType]) {

View File

@ -13,6 +13,10 @@
padding: 5px; padding: 5px;
} }
.assets-list-div h3 {
text-transform: capitalize;
}
.assets-list-div i { .assets-list-div i {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
@ -26,6 +30,10 @@
margin-left: 10px; margin-left: 10px;
} }
.assets-list-div i span:first-of-type {
font-weight: bold;
}
.asset-download-button { .asset-download-button {
position: relative; position: relative;
width: 50px; width: 50px;

View File

@ -1,7 +1,7 @@
<div id="assets_ui"> <div id="assets_ui">
<div class="inline-drawer"> <div class="inline-drawer">
<div class="inline-drawer-toggle inline-drawer-header"> <div class="inline-drawer-toggle inline-drawer-header">
<b>Assets</b> <b>Download Extensions & Assets</b>
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div> <div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
</div> </div>
<div class="inline-drawer-content"> <div class="inline-drawer-content">

View File

@ -3,6 +3,8 @@ import { getContext, getApiUrl, extension_settings, doExtrasFetch, modules } fro
import { eventSource, event_types, extension_prompt_types, generateQuietPrompt, is_send_press, saveSettingsDebounced, substituteParams } from "../../../script.js"; import { eventSource, event_types, extension_prompt_types, generateQuietPrompt, is_send_press, saveSettingsDebounced, substituteParams } from "../../../script.js";
import { is_group_generating, selected_group } from "../../group-chats.js"; import { is_group_generating, selected_group } from "../../group-chats.js";
import { registerSlashCommand } from "../../slash-commands.js"; import { registerSlashCommand } from "../../slash-commands.js";
import { loadMovingUIState } from '../../power-user.js';
import { dragElement } from "../../RossAscends-mods.js";
export { MODULE_NAME }; export { MODULE_NAME };
const MODULE_NAME = '1_memory'; const MODULE_NAME = '1_memory';
@ -547,32 +549,119 @@ function setMemoryContext(value, saveToMessage) {
} }
} }
function doPopout(e) {
const target = e.target;
//repurposes the zoomed avatar template to server as a floating div
if ($("#summaryExtensionPopout").length === 0) {
console.debug('did not see popout yet, creating')
const originalHTMLClone = $(target).parent().parent().parent().find('.inline-drawer-content').html()
const originalElement = $(target).parent().parent().parent().find('.inline-drawer-content')
const template = $('#zoomed_avatar_template').html();
const controlBarHtml = `<div class="panelControlBar flex-container">
<div id="summaryExtensionPopoutheader" class="fa-solid fa-grip drag-grabber hoverglow"></div>
<div id="summaryExtensionPopoutClose" class="fa-solid fa-circle-xmark hoverglow"></div>
</div>`
const newElement = $(template);
newElement.attr('id', 'summaryExtensionPopout');
newElement.removeClass('zoomed_avatar')
newElement.empty()
const prevSummaryBoxContents = $('#memory_contents').val(); //copy summary box before emptying
originalElement.empty();
originalElement.html(`<div class="flex-container alignitemscenter justifyCenter wide100p"><small>Currently popped out</small></div>`)
newElement.append(controlBarHtml).append(originalHTMLClone)
$('body').append(newElement);
$("#summaryExtensionDrawerContents").addClass('scrollableInnerFull')
setMemoryContext(prevSummaryBoxContents, false); //paste prev summary box contents into popout box
setupListeners();
loadSettings();
loadMovingUIState();
$("#summaryExtensionPopout").fadeIn(250);
dragElement(newElement);
//setup listener for close button to restore extensions menu
$('#summaryExtensionPopoutClose').off('click').on('click', function () {
$("#summaryExtensionDrawerContents").removeClass('scrollableInnerFull')
const summaryPopoutHTML = $("#summaryExtensionDrawerContents")
$("#summaryExtensionPopout").fadeOut(250, () => {
originalElement.empty();
originalElement.html(summaryPopoutHTML);
$("#summaryExtensionPopout").remove()
})
loadSettings();
})
} else {
console.debug('saw existing popout, removing')
$("#summaryExtensionPopout").fadeOut(250, () => { $("#summaryExtensionPopoutClose").trigger('click') });
}
}
function setupListeners() {
//setup shared listeners for popout and regular ext menu
$('#memory_restore').off('click').on('click', onMemoryRestoreClick);
$('#memory_contents').off('click').on('input', onMemoryContentInput);
$('#memory_long_length').off('click').on('input', onMemoryLongInput);
$('#memory_short_length').off('click').on('input', onMemoryShortInput);
$('#memory_repetition_penalty').off('click').on('input', onMemoryRepetitionPenaltyInput);
$('#memory_temperature').off('click').on('input', onMemoryTemperatureInput);
$('#memory_length_penalty').off('click').on('input', onMemoryLengthPenaltyInput);
$('#memory_frozen').off('click').on('input', onMemoryFrozenInput);
$('#summary_source').off('click').on('change', onSummarySourceChange);
$('#memory_prompt_words').off('click').on('input', onMemoryPromptWordsInput);
$('#memory_prompt_interval').off('click').on('input', onMemoryPromptIntervalInput);
$('#memory_prompt').off('click').on('input', onMemoryPromptInput);
$('#memory_force_summarize').off('click').on('click', forceSummarizeChat);
$('#memory_template').off('click').on('input', onMemoryTemplateInput);
$('#memory_depth').off('click').on('input', onMemoryDepthInput);
$('input[name="memory_position"]').off('click').on('change', onMemoryPositionChange);
$('#memory_prompt_words_force').off('click').on('input', onMemoryPromptWordsForceInput);
$("#summarySettingsBlockToggle").off('click').on('click', function () {
console.log('saw settings button click')
$("#summarySettingsBlock").slideToggle(200, "swing"); //toggleClass("hidden");
});
}
jQuery(function () { jQuery(function () {
function addExtensionControls() { function addExtensionControls() {
const settingsHtml = ` const settingsHtml = `
<div id="memory_settings"> <div id="memory_settings">
<div class="inline-drawer"> <div class="inline-drawer">
<div class="inline-drawer-toggle inline-drawer-header"> <div class="inline-drawer-toggle inline-drawer-header">
<b>Summarize</b> <div class="flex-container alignitemscenter margin0"><b>Summarize</b><i id="summaryExtensionPopoutButton" class="fa-solid fa-window-restore menu_button margin0"></i></div>
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div> <div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
</div> </div>
<div class="inline-drawer-content"> <div class="inline-drawer-content">
<label for="summary_source">Summarization source:</label> <div id="summaryExtensionDrawerContents">
<label for="summary_source">Summarize with:</label>
<select id="summary_source"> <select id="summary_source">
<option value="main">Main API</option> <option value="main">Main API</option>
<option value="extras">Extras API</option> <option value="extras">Extras API</option>
</select> </select><br>
<label for="memory_contents">Current summary: </label>
<div class="flex-container justifyspacebetween alignitemscenter">
<span class="flex1">Current summary:</span>
<div id="memory_restore" class="menu_button flex1 margin0"><span>Restore Previous</span></div>
</div>
<textarea id="memory_contents" class="text_pole textarea_compact" rows="6" placeholder="Summary will be generated here..."></textarea> <textarea id="memory_contents" class="text_pole textarea_compact" rows="6" placeholder="Summary will be generated here..."></textarea>
<div class="memory_contents_controls"> <div class="memory_contents_controls">
<input id="memory_restore" class="menu_button" type="button" value="Restore previous state" /> <div id="memory_force_summarize" class="menu_button menu_button_icon">
<label for="memory_frozen"><input id="memory_frozen" type="checkbox" />Pause summarization</label> <i class="fa-solid fa-database"></i>
<span>Summarize now</span>
</div> </div>
<label for="memory_frozen"><input id="memory_frozen" type="checkbox" />Pause</label>
</div>
<div class="memory_contents_controls">
<div id="summarySettingsBlockToggle" class="menu_button">Settings</div>
</div>
<div id="summarySettingsBlock" style="display:none;">
<div class="memory_template"> <div class="memory_template">
<label for="memory_template">Injection template:</label> <label for="memory_template">Insertion string:</label>
<textarea id="memory_template" class="text_pole textarea_compact" rows="1" placeholder="Use {{summary}} macro to specify the position of summarized text."></textarea> <textarea id="memory_template" class="text_pole textarea_compact" rows="2" placeholder="{{summary}} will resolve to the current summary contents."></textarea>
</div> </div>
<label for="memory_position">Injection position:</label> <label for="memory_position">Position:</label>
<div class="radio_group"> <div class="radio_group">
<label> <label>
<input type="radio" name="memory_position" value="2" /> <input type="radio" name="memory_position" value="2" />
@ -591,21 +680,19 @@ jQuery(function () {
</div> </div>
<div data-source="main"> <div data-source="main">
<label for="memory_prompt" class="title_restorable"> <label for="memory_prompt" class="title_restorable">
Summarization Prompt Summary Prompt
<div id="memory_force_summarize" class="menu_button menu_button_icon">
<i class="fa-solid fa-database"></i>
<span>Generate now</span>
</div>
</label> </label>
<textarea id="memory_prompt" class="text_pole textarea_compact" rows="6" placeholder="This prompt will be used in summary generation. Insert {{words}} macro to use the "Number of words" parameter."></textarea> <textarea id="memory_prompt" class="text_pole textarea_compact" rows="6" placeholder="This prompt will be sent to AI to request the summary generation. {{words}} will resolve to the 'Number of words' parameter."></textarea>
<label for="memory_prompt_words">Number of words in the summary (<span id="memory_prompt_words_value"></span> words)</label> <label for="memory_prompt_words">Summary length (<span id="memory_prompt_words_value"></span> words)</label>
<input id="memory_prompt_words" type="range" value="${defaultSettings.promptWords}" min="${defaultSettings.promptMinWords}" max="${defaultSettings.promptMaxWords}" step="${defaultSettings.promptWordsStep}" /> <input id="memory_prompt_words" type="range" value="${defaultSettings.promptWords}" min="${defaultSettings.promptMinWords}" max="${defaultSettings.promptMaxWords}" step="${defaultSettings.promptWordsStep}" />
<label for="memory_prompt_interval">Update interval (<span id="memory_prompt_interval_value"></span> messages)</label> <label for="memory_prompt_interval">Update every <span id="memory_prompt_interval_value"></span> messages</label>
<small>Set to 0 to disable</small> <small>0 = disable</small>
<input id="memory_prompt_interval" type="range" value="${defaultSettings.promptInterval}" min="${defaultSettings.promptMinInterval}" max="${defaultSettings.promptMaxInterval}" step="${defaultSettings.promptIntervalStep}" /> <input id="memory_prompt_interval" type="range" value="${defaultSettings.promptInterval}" min="${defaultSettings.promptMinInterval}" max="${defaultSettings.promptMaxInterval}" step="${defaultSettings.promptIntervalStep}" />
<label for="memory_prompt_words_force">Force update after (<span id="memory_prompt_words_force_value"></span> words)</label> <label for="memory_prompt_words_force">Update every <span id="memory_prompt_words_force_value"></span> words</label>
<small>Set to 0 to disable</small> <small>0 = disable</small>
<input id="memory_prompt_words_force" type="range" value="${defaultSettings.promptForceWords}" min="${defaultSettings.promptMinForceWords}" max="${defaultSettings.promptMaxForceWords}" step="${defaultSettings.promptForceWordsStep}" /> <input id="memory_prompt_words_force" type="range" value="${defaultSettings.promptForceWords}" min="${defaultSettings.promptMinForceWords}" max="${defaultSettings.promptMaxForceWords}" step="${defaultSettings.promptForceWordsStep}" />
<small>If both sliders are non-zero, then both will trigger summary updates a their respective intervals.</small>
</div> </div>
<div data-source="extras"> <div data-source="extras">
<label for="memory_short_length">Chat to Summarize buffer length (<span id="memory_short_length_tokens"></span> tokens)</label> <label for="memory_short_length">Chat to Summarize buffer length (<span id="memory_short_length_tokens"></span> tokens)</label>
@ -622,25 +709,15 @@ jQuery(function () {
</div> </div>
</div> </div>
</div> </div>
</div>
</div>
`; `;
$('#extensions_settings2').append(settingsHtml); $('#extensions_settings2').append(settingsHtml);
$('#memory_restore').on('click', onMemoryRestoreClick); setupListeners();
$('#memory_contents').on('input', onMemoryContentInput); $("#summaryExtensionPopoutButton").off('click').on('click', function (e) {
$('#memory_long_length').on('input', onMemoryLongInput); doPopout(e);
$('#memory_short_length').on('input', onMemoryShortInput); e.stopPropagation();
$('#memory_repetition_penalty').on('input', onMemoryRepetitionPenaltyInput); });
$('#memory_temperature').on('input', onMemoryTemperatureInput);
$('#memory_length_penalty').on('input', onMemoryLengthPenaltyInput);
$('#memory_frozen').on('input', onMemoryFrozenInput);
$('#summary_source').on('change', onSummarySourceChange);
$('#memory_prompt_words').on('input', onMemoryPromptWordsInput);
$('#memory_prompt_interval').on('input', onMemoryPromptIntervalInput);
$('#memory_prompt').on('input', onMemoryPromptInput);
$('#memory_force_summarize').on('click', forceSummarizeChat);
$('#memory_template').on('input', onMemoryTemplateInput);
$('#memory_depth').on('input', onMemoryDepthInput);
$('input[name="memory_position"]').on('change', onMemoryPositionChange);
$('#memory_prompt_words_force').on('input', onMemoryPromptWordsForceInput);
} }
addExtensionControls(); addExtensionControls();

View File

@ -1410,7 +1410,8 @@ async function resetMovablePanels(type) {
'WorldInfo', 'WorldInfo',
'floatingPrompt', 'floatingPrompt',
'expression-holder', 'expression-holder',
'groupMemberListPopout' 'groupMemberListPopout',
'summaryExtensionPopout'
]; ];
const panelStyles = ['top', 'left', 'right', 'bottom', 'height', 'width', 'margin',]; const panelStyles = ['top', 'left', 'right', 'bottom', 'height', 'width', 'margin',];

View File

@ -3200,6 +3200,12 @@ a {
height: calc(100% - 30px); height: calc(100% - 30px);
} }
.scrollableInnerFull {
height: 100%;
overflow-Y: auto;
overflow-X: hidden;
}
.fillLeft .scrollableInner { .fillLeft .scrollableInner {
padding: 0.5em 1em 0.5em 0.5em padding: 0.5em 1em 0.5em 0.5em
} }
@ -3404,7 +3410,8 @@ a {
} }
.zoomed_avatar, .zoomed_avatar,
#groupMemberListPopout { #groupMemberListPopout,
#summaryExtensionPopout {
min-width: 100px; min-width: 100px;
min-height: 100px; min-height: 100px;
max-height: 90vh; max-height: 90vh;
@ -3413,15 +3420,25 @@ a {
/* width: calc((100vw - var(--sheldWidth)) /2); */ /* width: calc((100vw - var(--sheldWidth)) /2); */
position: absolute; position: absolute;
padding: 0; padding: 0;
filter: drop-shadow(2px 2px 2px var(--grey7070a)); /* filter: drop-shadow(2px 2px 2px var(--grey7070a)); */
box-shadow: 1px 2px 2px var(--black30a);
z-index: 29; z-index: 29;
overflow: hidden; overflow: hidden;
display: none; display: none;
bottom: 0; bottom: 0;
aspect-ratio: 2 / 3;
border-radius: 10px; border-radius: 10px;
} }
.zoomed_avatar {
aspect-ratio: 2 / 3;
}
#groupMemberListPopout,
#summaryExtensionPopout {
border: 1px solid var(--SmartThemeBorderColor);
padding: 5px;
}
.draggable { .draggable {
min-width: 100px; min-width: 100px;
min-height: 100px; min-height: 100px;
@ -3460,14 +3477,18 @@ a {
transition: all 250ms; transition: all 250ms;
} }
#groupMemberListPopout { #groupMemberListPopout,
#summaryExtensionPopout {
aspect-ratio: unset; aspect-ratio: unset;
padding: 0;
backdrop-filter: blur(var(--SmartThemeBlurStrength)); backdrop-filter: blur(var(--SmartThemeBlurStrength));
background-color: var(--SmartThemeBlurTintColor); background-color: var(--SmartThemeBlurTintColor);
overflow: auto; overflow: auto;
} }
#groupMemberListPopout {
padding: 0;
}
#groupMemberListPopout #rm_group_members { #groupMemberListPopout #rm_group_members {
/* background-color: var(--SmartThemeBlurTintColor); */ /* background-color: var(--SmartThemeBlurTintColor); */
margin: 0; margin: 0;

View File

@ -1360,6 +1360,12 @@ app.post("/getcharacters", jsonParser, function (request, response) {
let processingPromises = pngFiles.map((file, index) => processCharacter(file, index)); let processingPromises = pngFiles.map((file, index) => processCharacter(file, index));
await Promise.all(processingPromises); performance.mark('B'); await Promise.all(processingPromises); performance.mark('B');
// Filter out invalid/broken characters
characters = Object.values(characters).filter(x => x?.name).reduce((acc, val, index) => {
acc[index] = val;
return acc;
}, {});
response.send(JSON.stringify(characters)); response.send(JSON.stringify(characters));
}); });
}); });