mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
Merge branch 'dev' of https://github.com/BlipRanger/SillyTavern into feature/random
This commit is contained in:
@ -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> Tokens (<div class="neutral_warning">${perm_tokens}</div><div> 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"));
|
||||
|
@ -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">
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"display_name": "Character Backgrounds",
|
||||
"display_name": "Chat Backgrounds",
|
||||
"loading_order": 7,
|
||||
"requires": [],
|
||||
"optional": [],
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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();
|
||||
|
||||
|
@ -2,6 +2,5 @@
|
||||
width: fit-content;
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: baseline;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
@ -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,
|
||||
});
|
||||
}
|
||||
})
|
||||
|
@ -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
|
||||
|
@ -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
2
public/scripts/select2.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -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(/\.[^/.]+$/, "")
|
||||
|
@ -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');
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
Reference in New Issue
Block a user