Merge branch 'dev' of https://github.com/BlipRanger/SillyTavern into feature/random

This commit is contained in:
BlipRanger
2023-06-27 20:18:55 -04:00
26 changed files with 1922 additions and 804 deletions

View File

@ -11,8 +11,8 @@ import {
is_send_press,
getTokenCount,
menu_type,
max_context,
saveSettingsDebounced,
} from "../script.js";
@ -287,18 +287,18 @@ export function RA_CountCharTokens() {
} else { console.debug("RA_TC -- no valid char found, closing."); }
}
// display the counted tokens
if (count_tokens < 1024 && perm_tokens < 1024) {
//display normal if both counts are under 1024
const tokenLimit = Math.max(((main_api !== 'openai' ? max_context : oai_settings.openai_max_context) / 2), 1024);
if (count_tokens < tokenLimit && perm_tokens < tokenLimit) {
$("#result_info").html(`<small>${count_tokens} Tokens (${perm_tokens} Permanent)</small>`);
} else {
$("#result_info").html(`
<div class="flex-container flexFlowColumn alignitemscenter">
<div class="flex-container alignitemscenter">
<div class="flex-container flexnowrap flexNoGap">
<small class="flex-container flexnowrap flexNoGap">
<div class="neutral_warning">${count_tokens}</div>&nbsp;Tokens (<div class="neutral_warning">${perm_tokens}</div><div>&nbsp;Permanent)</div>
</small>
</div>
<div id="chartokenwarning" class="menu_button whitespacenowrap"><a href="https://docs.sillytavern.app/usage/core-concepts/characterdesign/#character-tokens" target="_blank">About Token 'Limits'</a></div>
<div id="chartokenwarning" class="menu_button margin0 whitespacenowrap"><a href="https://docs.sillytavern.app/usage/core-concepts/characterdesign/#character-tokens" target="_blank">About Token 'Limits'</a></div>
</div>`);
} //warn if either are over 1024
}
@ -473,60 +473,97 @@ function OpenNavPanels() {
// Make the DIV element draggable:
dragElement(document.getElementById("sheld"));
dragElement(document.getElementById("left-nav-panel"));
dragElement(document.getElementById("right-nav-panel"));
dragElement(document.getElementById("avatar_zoom_popup"));
dragElement(document.getElementById("WorldInfo"));
// SECOND UPDATE AIMING FOR MUTATIONS ONLY
export function dragElement(elmnt) {
var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
if (document.getElementById(elmnt.id + "header")) { //ex: id="sheldheader"
// if present, the header is where you move the DIV from, but this overrides everything else:
document.getElementById(elmnt.id + "header").onmousedown = dragMouseDown;
var height, width, top, left;
var elmntName = elmnt.attr('id');
const elmntNameEscaped = $.escapeSelector(elmntName);
const elmntHeader = $(`#${elmntNameEscaped}header`);
if (elmntHeader.length) {
elmntHeader.off('mousedown').on('mousedown', (e) => {
dragMouseDown(e);
});
} else {
// otherwise, move the DIV from anywhere inside the DIV, b:
elmnt.onmousedown = dragMouseDown;
elmnt.off('mousedown').on('mousedown', dragMouseDown);
}
const observer = new MutationObserver((mutations) => {
const target = mutations[0].target;
if (!$(target).is(':visible')
|| $(target).hasClass('resizing')
|| Number((String(target.height).replace('px', ''))) < 50
|| Number((String(target.width).replace('px', ''))) < 50
|| isMobile() === true
) { return }
const style = getComputedStyle(target);
console.log(style.top, style.left)
height = target.offsetHeight;
width = target.offsetWidth;
top = parseInt(style.top);
left = parseInt(style.left);
/* console.log(`
height=${height},
width=${width},
top=${top},
left=${left}`); */
if (!power_user.movingUIState[elmntName]) {
power_user.movingUIState[elmntName] = {};
}
power_user.movingUIState[elmntName].top = top;
power_user.movingUIState[elmntName].left = left;
power_user.movingUIState[elmntName].width = width;
power_user.movingUIState[elmntName].height = height;
power_user.movingUIState[elmntName].right = 'unset';
power_user.movingUIState[elmntName].bottom = 'unset';
power_user.movingUIState[elmntName].margin = 'unset';
saveSettingsDebounced();
saveSettingsDebounced();
// Check if the element header exists and set the listener on the grabber
if (elmntHeader.length) {
elmntHeader.off('mousedown').on('mousedown', (e) => {
dragMouseDown(e);
});
} else {
elmnt.off('mousedown').on('mousedown', dragMouseDown);
}
});
observer.observe(elmnt.get(0), { attributes: true, attributeFilter: ['style'] });
function dragMouseDown(e) {
//console.log(e);
e = e || window.event;
e.preventDefault();
// get the mouse cursor position at startup:
pos3 = e.clientX; //mouse X at click
pos4 = e.clientY; //mouse Y at click
document.onmouseup = closeDragElement;
// call a function whenever the cursor moves:
document.onmousemove = elementDrag;
if (e) {
e.preventDefault();
pos3 = e.clientX; //mouse X at click
pos4 = e.clientY; //mouse Y at click
}
$(document).on('mouseup', closeDragElement);
$(document).on('mousemove', elementDrag);
}
function elementDrag(e) {
//disable scrollbars when dragging to prevent jitter
$("body").css("overflow", "hidden");
if (!power_user.movingUIState[elmntName]) {
power_user.movingUIState[elmntName] = {};
}
//get window size
let winWidth = window.innerWidth;
let winHeight = window.innerHeight;
//get necessary data for calculating element footprint
let draggableHeight = parseInt(getComputedStyle(elmnt).getPropertyValue('height').slice(0, -2));
let draggableWidth = parseInt(getComputedStyle(elmnt).getPropertyValue('width').slice(0, -2));
let draggableTop = parseInt(getComputedStyle(elmnt).getPropertyValue('top').slice(0, -2));
let draggableLeft = parseInt(getComputedStyle(elmnt).getPropertyValue('left').slice(0, -2));
let sheldWidth = parseInt(getComputedStyle(document.documentElement).getPropertyValue('--sheldWidth').slice(0, -2));
let sheldWidth = parseInt($('html').css('--sheldWidth').slice(0, -2));
let topBarFirstX = (winWidth - sheldWidth) / 2;
let topBarLastX = topBarFirstX + sheldWidth;
let maxX = (width + left);
let maxY = (height + top);
//set the lowest and most-right pixel the element touches
let maxX = (draggableWidth + draggableLeft);
let maxY = (draggableHeight + draggableTop);
// calculate the new cursor position:
e = e || window.event;
e.preventDefault();
@ -535,88 +572,54 @@ export function dragElement(elmnt) {
pos3 = e.clientX; //new mouse X
pos4 = e.clientY; //new mouse Y
elmnt.setAttribute('data-dragged', 'true');
elmnt.attr('data-dragged', 'true');
//fix over/underflows:
setTimeout(function () {
if (elmnt.offsetTop < 40) {
/* console.log('6'); */
if (maxX > topBarFirstX && maxX < topBarLastX) {
/* console.log('maxX inside topBar!'); */
elmnt.style.top = "42px";
}
if (elmnt.offsetLeft < topBarLastX && elmnt.offsetLeft > topBarFirstX) {
/* console.log('offsetLeft inside TopBar!'); */
elmnt.style.top = "42px";
}
if (elmnt.offset().top < 40) {
if (maxX > topBarFirstX && maxX < topBarLastX) {
elmnt.css('top', '42px');
}
if (elmnt.offsetTop - pos2 <= 0) {
/* console.log('1'); */
//prevent going out of window top + 42px barrier for TopBar (can hide grabber)
elmnt.style.top = "0px";
if (elmnt.offset().left < topBarLastX && elmnt.offset().left > topBarFirstX) {
elmnt.css('top', '42px');
}
if (elmnt.offsetLeft - pos1 <= 0) {
/* console.log('2'); */
//prevent moving out of window left
elmnt.style.left = "0px";
}
if (elmnt.offset().top - pos2 <= 0) {
elmnt.css('top', '0px');
}
if (elmnt.offset().left - pos1 <= 0) {
elmnt.css('left', '0px');
}
if (maxX >= winWidth) {
elmnt.css('left', elmnt.offset().left - 10 + "px");
}
if (maxY >= winHeight) {
elmnt.css('top', elmnt.offset().top - 10 + "px");
if (elmnt.offset().top - pos2 <= 40) {
elmnt.css('top', '20px');
}
}
elmnt.css('left', (elmnt.offset().left - pos1) + "px");
elmnt.css("top", (elmnt.offset().top - pos2) + "px");
elmnt.css('margin', 'unset');
if (maxX >= winWidth) {
/* console.log('3'); */
//bounce off right
elmnt.style.left = elmnt.offsetLeft - 10 + "px";
}
if (maxY >= winHeight) {
/* console.log('4'); */
//bounce off bottom
elmnt.style.top = elmnt.offsetTop - 10 + "px";
if (elmnt.offsetTop - pos2 <= 40) {
/* console.log('5'); */
//prevent going out of window top + 42px barrier for TopBar (can hide grabber)
/* console.log('caught Y bounce to <40Y top'); */
elmnt.style.top = "20px";
}
}
// if no problems, set element's new position
/* console.log('7'); */
elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
$(elmnt).css("bottom", "unset");
$(elmnt).css("right", "unset");
$(elmnt).css("margin", "unset");
/* console.log(`
offsetLeft: ${elmnt.offsetLeft}, offsetTop: ${elmnt.offsetTop}
winWidth: ${winWidth}, winHeight: ${winHeight}
sheldWidth: ${sheldWidth}
X: ${elmnt.style.left}
Y: ${elmnt.style.top}
MaxX: ${maxX}, MaxY: ${maxY}
Topbar 1st X: ${((winWidth - sheldWidth) / 2)}
TopBar lastX: ${((winWidth - sheldWidth) / 2) + sheldWidth}
`); */
}, 50)
/* console.log("left/top: " + (elmnt.offsetLeft - pos1) + "/" + (elmnt.offsetTop - pos2) +
", win: " + winWidth + "/" + winHeight +
", max X / Y: " + maxX + " / " + maxY); */
/*
console.log(`
winWidth: ${winWidth}, winHeight: ${winHeight}
sheldWidth: ${sheldWidth}
X: ${$(elmnt).css('left')}
Y: ${$(elmnt).css('top')}
MaxX: ${maxX}, MaxY: ${maxY}
Topbar 1st X: ${((winWidth - sheldWidth) / 2)}
TopBar lastX: ${((winWidth - sheldWidth) / 2) + sheldWidth}
`);
*/
}
function closeDragElement() {
// stop moving when mouse button is released:
document.onmouseup = null;
document.onmousemove = null;
//revert scrolling to normal after drag to allow recovery of vastly misplaced elements
$("body").css("overflow", "auto");
$(document).off('mouseup', closeDragElement);
$(document).off('mousemove', elementDrag);
$("body").css("overflow", "");
// Clear the "data-dragged" attribute
elmnt.attr('data-dragged', 'false');
}
}
@ -626,7 +629,22 @@ export function dragElement(elmnt) {
$("document").ready(function () {
// initial status check
setTimeout(RA_checkOnlineStatus, 100);
setTimeout(() => {
if (isMobile === false) {
dragElement($("#sheld"));
dragElement($("#left-nav-panel"));
dragElement($("#right-nav-panel"));
dragElement($("#WorldInfo"));
dragElement($("#floatingPrompt"));
}
RA_checkOnlineStatus
}
, 100);
//$('div').on('resize', saveMovingUIState());
// read the state of AutoConnect and AutoLoadChat.
$(AutoConnectCheckbox).prop("checked", LoadLocalBool("AutoConnectEnabled"));

View File

@ -120,7 +120,7 @@ $(document).ready(function () {
<div class="background_settings">
<div class="inline-drawer">
<div class="inline-drawer-toggle inline-drawer-header">
<b>Character Backgrounds</b>
<b>Chat Backgrounds</b>
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
</div>
<div class="inline-drawer-content">

View File

@ -1,5 +1,5 @@
{
"display_name": "Character Backgrounds",
"display_name": "Chat Backgrounds",
"loading_order": 7,
"requires": [],
"optional": [],

View File

@ -10,7 +10,7 @@ import { selected_group } from "../../group-chats.js";
import { ModuleWorkerWrapper, extension_settings, getContext, saveMetadataDebounced } from "../../extensions.js";
import { registerSlashCommand } from "../../slash-commands.js";
import { getCharaFilename, debounce } from "../../utils.js";
export { MODULE_NAME };
export { MODULE_NAME as NOTE_MODULE_NAME };
const MODULE_NAME = '2_floating_prompt'; // <= Deliberate, for sorting lower than memory
const UPDATE_INTERVAL = 1000;
@ -222,6 +222,8 @@ function loadSettings() {
export function setFloatingPrompt() {
const context = getContext();
if (!context.groupId && context.characterId === undefined) {
console.debug('setFloatingPrompt: Not in a chat. Skipping.');
shouldWIAddPrompt = false;
return;
}
@ -243,6 +245,7 @@ export function setFloatingPrompt() {
if (lastMessageNumber <= 0 || chat_metadata[metadata_keys.interval] <= 0) {
context.setExtensionPrompt(MODULE_NAME, '');
$('#extension_floating_counter').text('(disabled)');
shouldWIAddPrompt = false;
return;
}

View File

@ -111,9 +111,9 @@ async function loadSettings() {
$('#chromadb_n_results').val(extension_settings.chromadb.n_results).trigger('input');
$('#chromadb_split_length').val(extension_settings.chromadb.split_length).trigger('input');
$('#chromadb_file_split_length').val(extension_settings.chromadb.file_split_length).trigger('input');
$('#chromadb_keep_context_proportion').val(extension_settings.chromadb.keep_context_proportion).trigger('input');
$('#chromadb_custom_depth').val(extension_settings.chromadb.chroma_depth).trigger('input');
$('#chromadb_custom_msg').val(extension_settings.chromadb.recall_msg).trigger('input');
$('#chromadb_keep_context_proportion').val(extension_settings.chromadb.keep_context_proportion).trigger('input');
$('#chromadb_custom_depth').val(extension_settings.chromadb.chroma_depth).trigger('input');
$('#chromadb_custom_msg').val(extension_settings.chromadb.recall_msg).trigger('input');
$('#chromadb_auto_adjust').prop('checked', extension_settings.chromadb.auto_adjust);
$('#chromadb_freeze').prop('checked', extension_settings.chromadb.freeze);
enableDisableSliders();
@ -407,7 +407,7 @@ async function queryMultiMessages(chat_id, query) {
url.pathname = '/api/chromadb/multiquery';
const queryMessagesResult = await fetch(url, {
method: 'POST',
method: 'POST',
body: JSON.stringify({ chat_list, query, n_results: extension_settings.chromadb.n_results }),
headers: postHeaders,
});
@ -619,7 +619,7 @@ window.chromadb_interceptGeneration = async (chat, maxContext) => {
//prototype chroma duplicate removal
let chatset = new Set(chat.map(obj => obj.mes));
newChat = newChat.filter(obj => !chatset.has(obj.mes));
if(chromaDepth === -1){
chat.splice(chat.length, 0, ...newChat);
}
@ -702,7 +702,6 @@ jQuery(async () => {
<option value="date">Sort memories by date</option>
<option value="distance">Sort memories by relevance</option>
</select>
<label for="chromadb_keep_context">How many original chat messages to keep: (<span id="chromadb_keep_context_value"></span>) messages</label>
<label for="chromadb_keep_context"><small>How many original chat messages to keep: (<span id="chromadb_keep_context_value"></span>) messages</small></label>
<input id="chromadb_keep_context" type="range" min="${defaultSettings.keep_context_min}" max="${defaultSettings.keep_context_max}" step="${defaultSettings.keep_context_step}" value="${defaultSettings.keep_context}" />
<label for="chromadb_n_results"><small>Maximum number of ChromaDB 'memories' to inject: (<span id="chromadb_n_results_value"></span>) messages</small></label>

View File

@ -9,6 +9,7 @@ import {
updateMessageBlock,
} from "../../../script.js";
import { extension_settings, getContext } from "../../extensions.js";
import { secret_state, writeSecret } from "../../secrets.js";
const autoModeOptions = {
NONE: 'none',
@ -134,6 +135,14 @@ const languageCodes = {
'Zulu': 'zu',
};
const KEY_REQUIRED = ['deepl'];
function showKeyButton() {
const providerRequiresKey = KEY_REQUIRED.includes(extension_settings.translate.provider);
$("#translate_key_button").toggle(providerRequiresKey);
$("#translate_key_button").toggleClass('success', Boolean(secret_state[extension_settings.translate.provider]));
}
function loadSettings() {
for (const key in defaultSettings) {
if (!extension_settings.translate.hasOwnProperty(key)) {
@ -144,6 +153,7 @@ function loadSettings() {
$(`#translation_provider option[value="${extension_settings.translate.provider}"]`).attr('selected', true);
$(`#translation_target_language option[value="${extension_settings.translate.target_language}"]`).attr('selected', true);
$(`#translation_auto_mode option[value="${extension_settings.translate.auto_mode}"]`).attr('selected', true);
showKeyButton();
}
async function translateImpersonate(text) {
@ -186,18 +196,39 @@ async function translateProviderGoogle(text, lang) {
throw new Error(response.statusText);
}
async function translateProviderDeepl(text, lang) {
if (!secret_state.deepl) {
throw new Error('No DeepL API key');
}
const response = await fetch('/deepl_translate', {
method: 'POST',
headers: getRequestHeaders(),
body: JSON.stringify({ text: text, lang: lang }),
});
if (response.ok) {
const result = await response.text();
return result;
}
throw new Error(response.statusText);
}
async function translate(text, lang) {
try {
switch (extension_settings.translate.provider) {
case 'google':
return await translateProviderGoogle(text, lang);
case 'deepl':
return await translateProviderDeepl(text, lang);
default:
console.error('Unknown translation provider', extension_settings.translate.provider);
return text;
}
} catch (error) {
console.log(error);
toastr.error('Failed to translate message');
toastr.error(String(error), 'Failed to translate message');
}
}
@ -331,9 +362,13 @@ jQuery(() => {
<option value="both">Translate both</option>
</select>
<label for="translation_provider">Provider</label>
<select id="translation_provider" name="provider">
<option value="google">Google</option>
<select>
<div class="flex-container gap5px flexnowrap marginBot5">
<select id="translation_provider" name="provider" class="margin0">
<option value="google">Google</option>
<option value="deepl">DeepL</option>
<select>
<div id="translate_key_button" class="menu_button fa-solid fa-key margin0"></div>
</div>
<label for="translation_target_language">Target Language</label>
<select id="translation_target_language" name="target_language"></select>
<div id="translation_clear" class="menu_button">
@ -364,6 +399,7 @@ jQuery(() => {
});
$('#translation_provider').on('change', (event) => {
extension_settings.translate.provider = event.target.value;
showKeyButton();
saveSettingsDebounced();
});
$('#translation_target_language').on('change', (event) => {
@ -371,6 +407,17 @@ jQuery(() => {
saveSettingsDebounced();
});
$(document).on('click', '.mes_translate', onMessageTranslateClick);
$('#translate_key_button').on('click', async () => {
const optionText = $('#translation_provider option:selected').text();
const key = await callPopup(`<h3>${optionText} API Key</h3>`, 'input');
if (key == false) {
return;
}
await writeSecret(extension_settings.translate.provider, key);
toastr.success('API Key saved');
});
loadSettings();

View File

@ -2,6 +2,5 @@
width: fit-content;
display: flex;
gap: 10px;
align-items: baseline;
flex-direction: row;
}

View File

@ -225,12 +225,10 @@ async function showKudos() {
}
jQuery(function () {
let hordeModelSelectScrollTop = null;
$("#horde_model").on('mousedown change', async function (e) {
//desktop-only routine for multi-select without CTRL
if (deviceInfo.device.type === 'desktop') {
/*if (deviceInfo.device.type === 'desktop') {
let hordeModelSelectScrollTop = null;
e.preventDefault();
const option = $(e.target);
const selectElement = $(this)[0];
@ -238,7 +236,7 @@ jQuery(function () {
option.prop('selected', !option.prop('selected'));
await delay(1);
selectElement.scrollTop = hordeModelSelectScrollTop;
}
}*/
horde_settings.models = $('#horde_model').val();
console.log('Updated Horde models', horde_settings.models);
});
@ -265,4 +263,14 @@ jQuery(function () {
$("#horde_refresh").on("click", getHordeModels);
$("#horde_kudos").on("click", showKudos);
// Not needed on mobile
if (deviceInfo.device.type === 'desktop') {
$('#horde_model').select2({
width: '100%',
placeholder: 'Select Horde models',
allowClear: true,
closeOnSelect: false,
});
}
})

View File

@ -927,16 +927,15 @@ function getTokenizerModel() {
return turboTokenizer;
}
else if (oai_settings.windowai_model.includes('claude')) {
return turboTokenizer;
return 'claude';
}
else if (oai_settings.windowai_model.includes('GPT-NeoXT')) {
return 'gpt2';
}
}
// We don't have a Claude tokenizer for JS yet. Turbo 3.5 should be able to handle this.
if (oai_settings.chat_completion_source == chat_completion_sources.CLAUDE) {
return turboTokenizer;
return 'claude';
}
// Default to Turbo 3.5

View File

@ -12,12 +12,13 @@ import {
eventSource,
event_types,
getCurrentChatId,
printCharacters,
name1,
name2,
replaceCurrentChat,
setCharacterId
} from "../script.js";
import { favsToHotswap } from "./RossAscends-mods.js";
import { favsToHotswap, isMobile } from "./RossAscends-mods.js";
import {
groups,
selected_group,
@ -27,6 +28,7 @@ import { registerSlashCommand } from "./slash-commands.js";
export {
loadPowerUserSettings,
loadMovingUIState,
collapseNewlines,
playMessageSound,
sortGroupMembers,
@ -77,6 +79,13 @@ const send_on_enter_options = {
ENABLED: 1,
}
export const persona_description_positions = {
BEFORE_CHAR: 0,
AFTER_CHAR: 1,
TOP_AN: 2,
BOTTOM_AN: 3,
}
let power_user = {
tokenizer: tokenizers.CLASSIC,
token_padding: 64,
@ -101,6 +110,7 @@ let power_user = {
chat_display: chat_styles.DEFAULT,
sheld_width: sheld_width.DEFAULT,
never_resize_avatars: false,
show_card_avatar_urls: false,
play_message_sound: false,
play_sound_unfocused: true,
auto_save_msg_edits: false,
@ -122,6 +132,7 @@ let power_user = {
waifuMode: false,
movingUI: false,
movingUIState: {},
noShadows: false,
theme: 'Default (Dark) 1.7.1',
@ -160,6 +171,10 @@ let power_user = {
personas: {},
default_persona: null,
persona_descriptions: {},
persona_description: '',
persona_description_position: persona_description_positions.BEFORE_CHAR,
};
let themes = [];
@ -279,9 +294,15 @@ function toggleWaifu() {
}
function switchWaifuMode() {
//console.log(`switching waifu to ${power_user.waifuMode}`);
$("body").toggleClass("waifuMode", power_user.waifuMode);
$("#waifuMode").prop("checked", power_user.waifuMode);
if (isMobile() && !$('body').hasClass('waifuMode')) {
console.debug('saw mobile regular mode, removing ZoomedAvatars');
if ($('.zoomed_avatar[forChar]').length) {
$('.zoomed_avatar[forChar]').remove();
}
return;
}
scrollChatToBottom();
}
@ -575,6 +596,7 @@ function loadPowerUserSettings(settings, data) {
$("#play_message_sound").prop("checked", power_user.play_message_sound);
$("#play_sound_unfocused").prop("checked", power_user.play_sound_unfocused);
$("#never_resize_avatars").prop("checked", power_user.never_resize_avatars);
$("#show_card_avatar_urls").prop("checked", power_user.show_card_avatar_urls);
$("#auto_save_msg_edits").prop("checked", power_user.auto_save_msg_edits);
$("#allow_name1_display").prop("checked", power_user.allow_name1_display);
$("#allow_name2_display").prop("checked", power_user.allow_name2_display);
@ -621,6 +643,31 @@ function loadPowerUserSettings(settings, data) {
loadInstructMode();
loadMaxContextUnlocked();
switchWaifuMode();
loadMovingUIState();
//console.log(power_user)
}
function loadMovingUIState() {
if (isMobile() === false && power_user.movingUIState) {
for (var elmntName of Object.keys(power_user.movingUIState)) {
var elmntState = power_user.movingUIState[elmntName];
try {
var elmnt = $('#' + $.escapeSelector(elmntName));
if (elmnt.length) {
console.debug(`loading state for ${elmntName}`)
elmnt.css(elmntState);
} else {
console.debug(`skipping ${elmntName} because it doesn't exist in the DOM`)
}
} catch (err) {
console.debug(`error occurred while processing ${elmntName}: ${err}`)
}
}
} else {
console.debug('skipping movingUI state load for mobile')
return
}
}
function loadMaxContextUnlocked() {
@ -893,21 +940,26 @@ function resetMovablePanels() {
document.getElementById("right-nav-panel").style.width = '';
document.getElementById("right-nav-panel").style.margin = '';
document.getElementById("expression-holder").style.top = '';
document.getElementById("expression-holder").style.left = '';
document.getElementById("expression-holder").style.right = '';
document.getElementById("expression-holder").style.bottom = '';
document.getElementById("expression-holder").style.height = '';
document.getElementById("expression-holder").style.width = '';
document.getElementById("expression-holder").style.margin = '';
if ($("#expression-holder")) {
document.getElementById("expression-holder").style.top = '';
document.getElementById("expression-holder").style.left = '';
document.getElementById("expression-holder").style.right = '';
document.getElementById("expression-holder").style.bottom = '';
document.getElementById("expression-holder").style.height = '';
document.getElementById("expression-holder").style.width = '';
document.getElementById("expression-holder").style.margin = '';
}
if ($(".zoomed_avatar")) {
$(".zoomed_avatar").css('top', '');
$(".zoomed_avatar").css('left', '');
$(".zoomed_avatar").css('right', '');
$(".zoomed_avatar").css('bottom', '');
$(".zoomed_avatar").css('width', '');
$(".zoomed_avatar").css('height', '');
$(".zoomed_avatar").css('margin', '');
}
document.getElementById("avatar_zoom_popup").style.top = '';
document.getElementById("avatar_zoom_popup").style.left = '';
document.getElementById("avatar_zoom_popup").style.right = '';
document.getElementById("avatar_zoom_popup").style.bottom = '';
document.getElementById("avatar_zoom_popup").style.height = '';
document.getElementById("avatar_zoom_popup").style.width = '';
document.getElementById("avatar_zoom_popup").style.margin = '';
document.getElementById("WorldInfo").style.top = '';
document.getElementById("WorldInfo").style.left = '';
@ -917,7 +969,17 @@ function resetMovablePanels() {
document.getElementById("WorldInfo").style.width = '';
document.getElementById("WorldInfo").style.margin = '';
document.getElementById("floatingPrompt").style.top = '';
document.getElementById("floatingPrompt").style.left = '';
document.getElementById("floatingPrompt").style.right = '';
document.getElementById("floatingPrompt").style.bottom = '';
document.getElementById("floatingPrompt").style.height = '';
document.getElementById("floatingPrompt").style.width = '';
document.getElementById("floatingPrompt").style.margin = '';
$('*[data-dragged="true"]').removeAttr('data-dragged');
power_user.movingUIState = {}
saveSettingsDebounced();
eventSource.emit(event_types.MOVABLE_PANELS_RESET);
}
@ -1159,6 +1221,11 @@ $(document).ready(() => {
power_user.never_resize_avatars = !!$(this).prop('checked');
saveSettingsDebounced();
});
$("#show_card_avatar_urls").on('input', function () {
power_user.show_card_avatar_urls = !!$(this).prop('checked');
printCharacters();
saveSettingsDebounced();
});
$("#play_message_sound").on('input', function () {
power_user.play_message_sound = !!$(this).prop('checked');

2
public/scripts/select2.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -457,9 +457,9 @@ export function isDataURL(str) {
return regex.test(str);
}
export function getCharaFilename() {
export function getCharaFilename(chid) {
const context = getContext();
const fileName = context.characters[context.characterId].avatar;
const fileName = context.characters[chid ?? context.characterId].avatar;
if (fileName) {
return fileName.replace(/\.[^/.]+$/, "")

View File

@ -1,8 +1,9 @@
import { saveSettings, callPopup, substituteParams, getTokenCount, getRequestHeaders, chat_metadata, this_chid, characters } from "../script.js";
import { download, debounce, initScrollHeight, resetScrollHeight, parseJsonFile, extractDataFromPng, getFileBuffer } from "./utils.js";
import { saveSettings, callPopup, substituteParams, getTokenCount, getRequestHeaders, chat_metadata, this_chid, characters, saveCharacterDebounced, menu_type } from "../script.js";
import { download, debounce, initScrollHeight, resetScrollHeight, parseJsonFile, extractDataFromPng, getFileBuffer, delay, getCharaFilename } from "./utils.js";
import { getContext } from "./extensions.js";
import { metadata_keys, shouldWIAddPrompt } from "./extensions/floating-prompt/index.js";
import { NOTE_MODULE_NAME, metadata_keys, shouldWIAddPrompt } from "./extensions/floating-prompt/index.js";
import { registerSlashCommand } from "./slash-commands.js";
import { deviceInfo } from "./RossAscends-mods.js";
export {
world_info,
@ -25,7 +26,8 @@ const world_info_insertion_strategy = {
global_first: 2,
};
let world_info = null;
let world_info = {};
let selected_world_info = [];
let world_names;
let world_info_depth = 2;
let world_info_budget = 25;
@ -34,7 +36,10 @@ let world_info_case_sensitive = false;
let world_info_match_whole_words = false;
let world_info_character_strategy = world_info_insertion_strategy.evenly;
const saveWorldDebounced = debounce(async (name, data) => await _save(name, data), 1000);
const saveSettingsDebounced = debounce(() => saveSettings(), 1000);
const saveSettingsDebounced = debounce(() => {
Object.assign(world_info, { globalSelect: selected_world_info })
saveSettings()
}, 1000);
const sortFn = (a, b) => b.order - a.order;
const world_info_position = {
@ -77,6 +82,19 @@ function setWorldInfoSettings(settings, data) {
world_info_budget = 25;
}
// Reset selected world from old string and delete old keys
// TODO: Remove next release
const existingWorldInfo = settings.world_info;
if (typeof existingWorldInfo === "string") {
delete settings.world_info;
selected_world_info = [existingWorldInfo];
} else if (Array.isArray(existingWorldInfo)) {
delete settings.world_info;
selected_world_info = existingWorldInfo;
}
world_info = settings.world_info ?? {}
$("#world_info_depth_counter").text(world_info_depth);
$("#world_info_depth").val(world_info_depth);
@ -92,22 +110,22 @@ function setWorldInfoSettings(settings, data) {
world_names = data.world_names?.length ? data.world_names : [];
if (settings.world_info != undefined) {
if (world_names.includes(settings.world_info)) {
world_info = settings.world_info;
}
// Add to existing selected WI if it exists
selected_world_info = selected_world_info.concat(settings.world_info?.globalSelect?.filter((e) => world_names.includes(e)) ?? []);
if (world_names.length > 0) {
$("#world_info").empty();
}
world_names.forEach((item, i) => {
$("#world_info").append(`<option value='${i}'>${item}</option>`);
$("#world_info").append(`<option value='${i}'${selected_world_info.includes(item) ? ' selected' : ''}>${item}</option>`);
$("#world_editor_select").append(`<option value='${i}'>${item}</option>`);
// preselect world if saved
if (item == world_info) {
$("#world_info").val(i).trigger('change');
}
});
$("#world_editor_select").trigger("change");
// Update settings
saveSettingsDebounced();
}
// World Info Editor
@ -160,7 +178,7 @@ async function updateWorldInfoList() {
$("#world_editor_select").find('option[value!=""]').remove();
world_names.forEach((item, i) => {
$("#world_info").append(`<option value='${i}'>${item}</option>`);
$("#world_info").append(`<option value='${i}'${selected_world_info.includes(item) ? ' selected' : ''}>${item}</option>`);
$("#world_editor_select").append(`<option value='${i}'>${item}</option>`);
});
}
@ -170,6 +188,14 @@ function hideWorldEditor() {
displayWorldEntries(null, null);
}
function getWIElement(name) {
const wiElement = $("#world_info").children().filter(function () {
return $(this).text().toLowerCase() === name.toLowerCase()
});
return wiElement;
}
function nullWorldInfo() {
toastr.info("Create or import a new World Info file first.", "World Info is not set", { timeOut: 10000, preventDuplicates: true });
}
@ -224,7 +250,21 @@ function displayWorldEntries(name, data) {
return;
}
await deleteWorldInfo(name, world_info);
const existingCharLores = world_info.charLore?.filter((e) => e.extraBooks.includes(name));
if (existingCharLores && existingCharLores.length > 0) {
existingCharLores.forEach((charLore) => {
const tempCharLore = charLore.extraBooks.filter((e) => e !== name);
if (tempCharLore.length === 0) {
world_info.charLore.splice(charLore, 1);
} else {
charLore.extraBooks = tempCharLore;
}
});
saveSettingsDebounced();
}
// Selected world_info automatically refreshes
await deleteWorldInfo(name);
});
// Check if a sortable instance exists
@ -256,7 +296,7 @@ function displayWorldEntries(name, data) {
await saveWorldInfo(name, data, true);
}
});
$("#world_popup_entries_list").disableSelection();
//$("#world_popup_entries_list").disableSelection();
}
function setOriginalDataValue(data, uid, key, value) {
@ -582,19 +622,26 @@ async function renameWorldInfo(name, data) {
return;
}
let selectNewName = null;
if (oldName === world_info) {
console.debug("Renaming current world info");
world_info = newName;
selectNewName = newName;
}
else {
console.debug("Renaming non-current world info");
selectNewName = world_info;
}
const entryPreviouslySelected = selected_world_info.findIndex((e) => e === oldName);
await saveWorldInfo(newName, data, true);
await deleteWorldInfo(oldName, selectNewName);
await deleteWorldInfo(oldName);
const existingCharLores = world_info.charLore?.filter((e) => e.extraBooks.includes(oldName));
if (existingCharLores && existingCharLores.length > 0) {
existingCharLores.forEach((charLore) => {
const tempCharLore = charLore.extraBooks.filter((e) => e !== oldName);
tempCharLore.push(newName);
charLore.extraBooks = tempCharLore;
});
saveSettingsDebounced();
}
if (entryPreviouslySelected !== -1) {
const wiElement = getWIElement(newName);
wiElement.prop("selected", true);
$("#world_info").trigger('change');
}
const selectedIndex = world_names.indexOf(newName);
if (selectedIndex !== -1) {
@ -602,7 +649,7 @@ async function renameWorldInfo(name, data) {
}
}
async function deleteWorldInfo(worldInfoName, selectWorldName) {
async function deleteWorldInfo(worldInfoName) {
if (!world_names.includes(worldInfoName)) {
return;
}
@ -614,16 +661,22 @@ async function deleteWorldInfo(worldInfoName, selectWorldName) {
});
if (response.ok) {
await updateWorldInfoList();
const selectedIndex = world_names.indexOf(selectWorldName);
if (selectedIndex !== -1) {
$("#world_info").val(selectedIndex).trigger('change');
} else {
$("#world_info").val("").trigger('change');
const existingWorldIndex = selected_world_info.findIndex((e) => e === worldInfoName);
if (existingWorldIndex !== -1) {
selected_world_info.splice(existingWorldIndex, 1);
saveSettingsDebounced();
}
await updateWorldInfoList();
$('#world_editor_select').trigger('change');
if ($('#character_world').val() === worldInfoName) {
$('#character_world').val('').trigger('change');
setWorldInfoButtonClass(undefined, false);
if (menu_type != 'create') {
saveCharacterDebounced();
}
}
}
}
@ -663,16 +716,13 @@ async function createNewWorldInfo(worldInfoName) {
return;
}
world_info = worldInfoName;
await saveWorldInfo(world_info, worldInfoTemplate, true);
await saveWorldInfo(worldInfoName, worldInfoTemplate, true);
await updateWorldInfoList();
const selectedIndex = world_names.indexOf(worldInfoName);
if (selectedIndex !== -1) {
$("#world_info").val(selectedIndex).trigger('change');
$('#world_editor_select').val(selectedIndex).trigger('change');
} else {
$("#world_info").val("").trigger('change');
hideWorldEditor();
}
}
@ -683,40 +733,53 @@ function transformString(str) {
}
async function getCharacterLore() {
const name = characters[this_chid]?.data?.extensions?.world;
const character = characters[this_chid];
const name = character?.name;
let worldsToSearch = new Set();
if (!name) {
const baseWorldName = character?.data?.extensions?.world;
if (baseWorldName) {
worldsToSearch.add(baseWorldName);
} else {
console.debug(`Character ${name}'s base world could not be found or is empty! Skipping...`)
return [];
}
if (name === world_info) {
console.debug(`Character ${characters[this_chid]?.name} world info is the same as global: ${name}. Skipping...`);
return [];
// TODO: Maybe make the utility function not use the window context?
const fileName = getCharaFilename(this_chid);
const extraCharLore = world_info.charLore?.find((e) => e.name === fileName);
if (extraCharLore) {
worldsToSearch = new Set([...worldsToSearch, ...extraCharLore.extraBooks]);
}
if (!world_names.includes(name)) {
console.log(`Character ${characters[this_chid]?.name} world info does not exist: ${name}`);
return [];
let entries = [];
for (const worldName of worldsToSearch) {
if (selected_world_info.includes(worldName)) {
console.debug(`Character ${name}'s world ${worldName} is already activated in global world info! Skipping...`);
continue;
}
const data = await loadWorldInfoData(worldName);
const newEntries = data ? Object.keys(data.entries).map((x) => data.entries[x]) : [];
entries = entries.concat(newEntries);
}
const data = await loadWorldInfoData(name);
const entries = data ? Object.keys(data.entries).map((x) => data.entries[x]) : [];
console.debug(`Character ${characters[this_chid]?.name} lore (${name}) has ${entries.length} world info entries`);
console.debug(`Character ${characters[this_chid]?.name} lore (${baseWorldName}) has ${entries.length} world info entries`);
return entries;
}
async function getGlobalLore() {
if (!world_info) {
if (!selected_world_info) {
return [];
}
if (!world_names.includes(world_info)) {
console.log(`Global ${characters[this_chid]?.name} world info does not exist: ${world_info}`);
return [];
let entries = [];
for (const worldName of selected_world_info) {
const data = await loadWorldInfoData(worldName);
const newEntries = data ? Object.keys(data.entries).map((x) => data.entries[x]) : [];
entries = entries.concat(newEntries);
}
const data = await loadWorldInfoData(world_info);
const entries = data ? Object.keys(data.entries).map((x) => data.entries[x]) : [];
console.debug(`Global world info has ${entries.length} entries`);
return entries;
@ -819,11 +882,12 @@ async function checkWorldInfo(chat, maxContext) {
const newEntries = [...activatedNow]
.sort((a, b) => sortedEntries.indexOf(a) - sortedEntries.indexOf(b));
let newContent = "";
const textToScanTokens = getTokenCount(textToScan);
for (const entry of newEntries) {
newContent += `${substituteParams(entry.content)}\n`;
if (getTokenCount(textToScan + newContent) >= budget) {
if (textToScanTokens + getTokenCount(newContent) >= budget) {
console.debug(`WI budget reached, stopping`);
needsToScan = false;
break;
@ -837,28 +901,39 @@ async function checkWorldInfo(chat, maxContext) {
}
}
let worldInfoBefore = "";
let worldInfoAfter = "";
const ANTopInjection = [];
const ANBottomInjection = [];
// Forward-sorted list of entries for joining
const WIBeforeEntries = [];
const WIAfterEntries = [];
const ANTopEntries = [];
const ANBottomEntries = [];
// Appends from insertion order 999 to 1. Use unshift for this purpose
[...allActivatedEntries].sort(sortFn).forEach((entry) => {
if (entry.position === world_info_position.before) {
worldInfoBefore = `${substituteParams(entry.content)}\n${worldInfoBefore}`;
} else if (entry.position === world_info_position.after) {
worldInfoAfter = `${substituteParams(entry.content)}\n${worldInfoAfter}`;
} else if (entry.position === world_info_position.ANTop) {
ANTopInjection.push(entry.content);
} else if (entry.position === world_info_position.ANBottom) {
ANBottomInjection.push(entry.content);
switch (entry.position) {
case world_info_position.before:
WIBeforeEntries.unshift(substituteParams(entry.content));
break;
case world_info_position.after:
WIAfterEntries.unshift(substituteParams(entry.content));
break;
case world_info_position.ANTop:
ANTopEntries.unshift(entry.content);
break;
case world_info_position.ANBottom:
ANBottomEntries.unshift(entry.content);
break;
default:
break;
}
});
const worldInfoBefore = `${WIBeforeEntries.join("\n")}\n`
const worldInfoAfter = `${WIAfterEntries.join("\n")}\n`
if (shouldWIAddPrompt) {
const originalAN = context.extensionPrompts['2_floating_prompt'].value;
const ANWithWI = `\n${ANTopInjection.join("\n")} \n${originalAN} \n${ANBottomInjection.reverse().join("\n")}`
context.setExtensionPrompt('2_floating_prompt', ANWithWI, chat_metadata[metadata_keys.position], chat_metadata[metadata_keys.depth]);
const originalAN = context.extensionPrompts[NOTE_MODULE_NAME].value;
const ANWithWI = `${ANTopEntries.join("\n")}\n${originalAN}\n${ANBottomEntries.join("\n")}`
context.setExtensionPrompt(NOTE_MODULE_NAME, ANWithWI, chat_metadata[metadata_keys.position], chat_metadata[metadata_keys.depth]);
}
return { worldInfoBefore, worldInfoAfter };
@ -993,6 +1068,48 @@ function convertCharacterBook(characterBook) {
return result;
}
export function setWorldInfoButtonClass(chid, forceValue = undefined) {
if (forceValue !== undefined) {
$('#set_character_world, #world_button').toggleClass('world_set', forceValue);
return;
}
if (!chid) {
return;
}
const world = characters[chid]?.data?.extensions?.world;
const worldSet = Boolean(world && world_names.includes(world));
$('#set_character_world, #world_button').toggleClass('world_set', worldSet);
}
export function checkEmbeddedWorld(chid) {
$('#import_character_info').hide();
if (chid === undefined) {
return false;
}
if (characters[chid]?.data?.character_book) {
$('#import_character_info').data('chid', chid).show();
// Only show the alert once per character
const checkKey = `AlertWI_${characters[chid].avatar}`;
const worldName = characters[chid]?.data?.extensions?.world;
if (!localStorage.getItem(checkKey) && (!worldName || !world_names.includes(worldName))) {
toastr.info(
'To import and use it, select "Import Embedded World Info" in the Options dropdown menu on the character panel.',
`${characters[chid].name} has an embedded World/Lorebook`,
{ timeOut: 10000, extendedTimeOut: 20000, positionClass: 'toast-top-center' },
);
localStorage.setItem(checkKey, 1);
}
return true;
}
return false;
}
export async function importEmbeddedWorldInfo() {
const chid = $('#import_character_info').data('chid');
@ -1021,40 +1138,148 @@ export async function importEmbeddedWorldInfo() {
if (newIndex >= 0) {
$("#world_editor_select").val(newIndex).trigger('change');
}
setWorldInfoButtonClass(chid, true);
}
function onWorldInfoChange(_, text) {
let selectedWorld;
if (_ !== '__notSlashCommand__') { //if it's a slash command
if (text !== undefined) { //and args are provided
let slashInputWorld = text.toLowerCase();
$("#world_info").find(`option`).filter(function () {
return $(this).text().toLowerCase() === slashInputWorld;
}).prop('selected', true); //matches arg with worldnames and selects; if none found, unsets world
let setWorldName = $("#world_info").find(":selected").text(); //only for toastr display
toastr.success(`Active world: ${setWorldName}`);
selectedWorld = $("#world_info").find(":selected").val();
} else { //if no args, unset world
toastr.success('Deselected World')
let selectedWorlds;
if (_ !== '__notSlashCommand__') { // if it's a slash command
if (text !== undefined) { // and args are provided
const slashInputSplitText = text.trim().toLowerCase().split(",");
slashInputSplitText.forEach((worldName) => {
const wiElement = getWIElement(worldName);
if (wiElement.length > 0) {
wiElement.prop("selected", true);
toastr.success(`Activated world: ${wiElement.text()}`);
} else {
toastr.error(`No world found named: ${worldName}`);
}
})
} else { // if no args, unset all worlds
toastr.success('Deactivated all worlds');
selected_world_info = [];
$("#world_info").val("");
}
} else { //if it's a pointer selection
selectedWorld = $("#world_info").find(":selected").val();
}
world_info = null;
if (selectedWorld !== "") {
const worldIndex = Number(selectedWorld);
world_info = !isNaN(worldIndex) ? world_names[worldIndex] : null;
let tempWorldInfo = [];
let selectedWorlds = $("#world_info").val().map((e) => Number(e)).filter((e) => !isNaN(e));
if (selectedWorlds.length > 0) {
selectedWorlds.forEach((worldIndex) => {
const existingWorldName = world_names[worldIndex];
if (existingWorldName) {
tempWorldInfo.push(existingWorldName);
} else {
const wiElement = getWIElement(existingWorldName);
wiElement.prop("selected", false);
toastr.error(`The world with ${existingWorldName} is invalid or corrupted.`);
}
});
}
selected_world_info = tempWorldInfo;
}
saveSettingsDebounced();
}
export async function importWorldInfo(file) {
if (!file) {
return;
}
const formData = new FormData();
formData.append('avatar', file);
try {
let jsonData;
if (file.name.endsWith('.png')) {
const buffer = new Uint8Array(await getFileBuffer(file));
jsonData = extractDataFromPng(buffer, 'naidata');
} else {
// File should be a JSON file
jsonData = await parseJsonFile(file);
}
if (jsonData === undefined || jsonData === null) {
toastr.error(`File is not valid: ${file.name}`);
return;
}
// Convert Novel Lorebook
if (jsonData.lorebookVersion !== undefined) {
console.log('Converting Novel Lorebook');
formData.append('convertedData', JSON.stringify(convertNovelLorebook(jsonData)));
}
// Convert Agnai Memory Book
if (jsonData.kind === 'memory') {
console.log('Converting Agnai Memory Book');
formData.append('convertedData', JSON.stringify(convertAgnaiMemoryBook(jsonData)));
}
// Convert Risu Lorebook
if (jsonData.type === 'risu') {
console.log('Converting Risu Lorebook');
formData.append('convertedData', JSON.stringify(convertRisuLorebook(jsonData)));
}
} catch (error) {
toastr.error(`Error parsing file: ${error}`);
return;
}
jQuery.ajax({
type: "POST",
url: "/importworldinfo",
data: formData,
beforeSend: () => { },
cache: false,
contentType: false,
processData: false,
success: async function (data) {
if (data.name) {
await updateWorldInfoList();
const newIndex = world_names.indexOf(data.name);
if (newIndex >= 0) {
$("#world_editor_select").val(newIndex).trigger('change');
}
toastr.info(`World Info "${data.name}" imported successfully!`);
}
},
error: (jqXHR, exception) => { },
});
}
jQuery(() => {
$(document).ready(function () {
registerSlashCommand('world', onWorldInfoChange, [], " sets active World, or unsets if no args provided", true, true);
})
$("#world_info").on('change', async function () { onWorldInfoChange('__notSlashCommand__') });
let selectScrollTop = null;
$("#world_info").on('mousedown change', async function (e) {
// If there's no world names, don't do anything
if (world_names.length === 0) {
e.preventDefault();
return;
}
if (deviceInfo.device.type === 'desktop') {
e.preventDefault();
const option = $(e.target);
const selectElement = $(this)[0];
selectScrollTop = selectElement.scrollTop;
option.prop('selected', !option.prop('selected'));
await delay(1);
selectElement.scrollTop = selectScrollTop;
}
onWorldInfoChange('__notSlashCommand__');
});
//**************************WORLD INFO IMPORT EXPORT*************************//
$("#world_import_button").on('click', function () {
@ -1064,81 +1289,12 @@ jQuery(() => {
$("#world_import_file").on("change", async function (e) {
const file = e.target.files[0];
if (!file) {
return;
}
const formData = new FormData($("#form_world_import").get(0));
try {
let jsonData;
if (file.name.endsWith('.png')) {
const buffer = new Uint8Array(await getFileBuffer(file));
jsonData = extractDataFromPng(buffer, 'naidata');
} else {
// File should be a JSON file
jsonData = await parseJsonFile(file);
}
if (jsonData === undefined || jsonData === null) {
toastr.error(`File is not valid: ${file.name}`);
return;
}
// Convert Novel Lorebook
if (jsonData.lorebookVersion !== undefined) {
console.log('Converting Novel Lorebook');
formData.append('convertedData', JSON.stringify(convertNovelLorebook(jsonData)));
}
// Convert Agnai Memory Book
if (jsonData.kind === 'memory') {
console.log('Converting Agnai Memory Book');
formData.append('convertedData', JSON.stringify(convertAgnaiMemoryBook(jsonData)));
}
// Convert Risu Lorebook
if (jsonData.type === 'risu') {
console.log('Converting Risu Lorebook');
formData.append('convertedData', JSON.stringify(convertRisuLorebook(jsonData)));
}
} catch (error) {
toastr.error(`Error parsing file: ${error}`);
return;
}
jQuery.ajax({
type: "POST",
url: "/importworldinfo",
data: formData,
beforeSend: () => { },
cache: false,
contentType: false,
processData: false,
success: async function (data) {
if (data.name) {
await updateWorldInfoList();
const newIndex = world_names.indexOf(data.name);
if (newIndex >= 0) {
$("#world_editor_select").val(newIndex).trigger('change');
}
toastr.info(`World Info "${data.name}" imported successfully!`);
}
},
error: (jqXHR, exception) => { },
});
await importWorldInfo(file);
// Will allow to select the same file twice in a row
$("#form_world_import").trigger("reset");
});
$("#world_cross").click(() => {
hideWorldEditor();
});
$("#world_create_button").on('click', async () => {
const tempName = getFreeWorldName();
const finalName = await callPopup("<h3>Create a new World Info?</h3>Enter a name for the new file:", "input", tempName);
@ -1189,5 +1345,27 @@ jQuery(() => {
$('#world_info_character_strategy').on('change', function () {
world_info_character_strategy = $(this).val();
saveSettingsDebounced();
})
});
$('#world_button').on('click', async function () {
const chid = $('#set_character_world').data('chid');
if (chid) {
const worldName = characters[chid]?.data?.extensions?.world;
const hasEmbed = checkEmbeddedWorld(chid);
if (worldName && world_names.includes(worldName)) {
if (!$('#WorldInfo').is(':visible')) {
$('#WIDrawerIcon').trigger('click');
}
const index = world_names.indexOf(worldName);
$("#world_editor_select").val(index).trigger('change');
} else if (hasEmbed) {
await importEmbeddedWorldInfo();
saveCharacterDebounced();
}
else {
$('#char-management-dropdown').val($('#set_character_world').val()).trigger('change');
}
}
});
});