mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
Split WI editor and global WI select
This commit is contained in:
@ -1826,34 +1826,48 @@
|
||||
</div>
|
||||
<div id="WorldInfo" class="drawer-content closedDrawer">
|
||||
<div id="WorldInfoheader" class="fa-solid fa-grip drag-grabber"></div>
|
||||
<div id="WI_panel_pin_div" title="Locked = World Editor will stay open">
|
||||
<div id="WI_panel_pin_div" class="flex-container alignitemscenter gap10px" title="Locked = World Editor will stay open">
|
||||
<input type="checkbox" id="WI_panel_pin">
|
||||
<label for="WI_panel_pin">
|
||||
<div class="unchecked fa-solid fa-unlock "></div>
|
||||
<div class="checked fa-solid fa-lock "></div>
|
||||
</label>
|
||||
</div>
|
||||
<div id="wi-holder" class="margin5">
|
||||
<h3>
|
||||
Global World Info / Lorebook Selector
|
||||
Worlds/Lorebooks
|
||||
<a href="https://docs.sillytavern.app/usage/core-concepts/worldinfo/" class="notes-link" target="_blank">
|
||||
<span class="note-link-span">?</span>
|
||||
</a>
|
||||
</h3>
|
||||
<form id="form_world_import" action="javascript:void(null);" method="post" enctype="multipart/form-data">
|
||||
<input type="file" id="world_import_file" accept=".json" name="avatar" hidden>
|
||||
</form>
|
||||
<div class="world_info_select_block">
|
||||
<select id="world_info" class="flexGrow">
|
||||
</div>
|
||||
<div id="wi-holder" class="margin5">
|
||||
<div class="wi-settings flex-container gap10px alignitemscenter">
|
||||
<div class="flex1 flex-container flexFlowColumn">
|
||||
<div class="flex range-block">
|
||||
<div class="range-block-title justifyLeft">
|
||||
<small>Active World</small>
|
||||
</div>
|
||||
<div class="range-block-range">
|
||||
<select id="world_info" class="flexGrow margin0">
|
||||
<option value="None">None</option>
|
||||
</select>
|
||||
<div id="world_create_button" class="menu_button fa-solid fa-plus faSmallFontSquareFix" title="Create"></div>
|
||||
<div id="world_import_button" class="menu_button fa-solid fa-file-arrow-up faSmallFontSquareFix" title="Import">
|
||||
</div>
|
||||
<div id="world_info_edit_button" class="menu_button fa-solid fa-pencil faSmallFontSquareFix" title="Details"></div>
|
||||
</div>
|
||||
<div class="flex range-block">
|
||||
<div class="range-block-title justifyLeft">
|
||||
<label for="world_info_character_strategy">
|
||||
<small>Character Lore Insertion Strategy</small>
|
||||
</label>
|
||||
</div>
|
||||
<div class="range-block-range">
|
||||
<select id="world_info_character_strategy" class="flexGrow margin0">
|
||||
<option value="0">Sorted Evenly</option>
|
||||
<option value="1">Character Lore First</option>
|
||||
<option value="2">Global Lore First</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex-container gap10px alignitemscenter">
|
||||
<div name="WIScanAndTokens" class="flex1 flex-container flexFlowColumn">
|
||||
<div class="flex1 gap5px range-block">
|
||||
<div class="wide10pMinFit">
|
||||
@ -1861,7 +1875,7 @@
|
||||
</div>
|
||||
<div class="range-block-range-and-counter">
|
||||
<div class="range-block-range paddingLeftRight5">
|
||||
<input type="range" id="world_info_depth" name="volume" min="1" max="10" step="1">
|
||||
<input type="range" id="world_info_depth" name="volume" min="0" max="10" step="1">
|
||||
</div>
|
||||
<div class="range-block-counter margin0">
|
||||
<div contenteditable="true" data-for="world_info_depth" id="world_info_depth_counter">
|
||||
@ -1887,18 +1901,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex1 flex-container flexFlowColumn">
|
||||
<div class="flex gap5px range-block">
|
||||
<label for="world_info_character_strategy">
|
||||
Character Lore Insertion Strategy
|
||||
</label>
|
||||
<select id="world_info_character_strategy" class="flexGrow">
|
||||
<option value="0">Sorted Evenly</option>
|
||||
<option value="1">Character Lore First</option>
|
||||
<option value="2">Global Lore First</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="range-block flex-container flexFlowColumn">
|
||||
<label title="Entries can activate other entries by mentioning their keywords" class="checkbox_label">
|
||||
<input id="world_info_recursive" type="checkbox" />
|
||||
@ -1927,23 +1929,25 @@
|
||||
<div id="world_popup_header" class="flex-container flexGap5">
|
||||
<div class="world_popup_logo_block">
|
||||
<h3>
|
||||
World Editor
|
||||
World/Lore Editor
|
||||
<a href="https://docs.sillytavern.app/usage/core-concepts/worldinfo/#world-info-entry" class="notes-link" target="_blank"><span class="note-link-span">?</span></a>
|
||||
</h3>
|
||||
</div>
|
||||
<div id="OpenAllWIEntries" class="menu_button fa-solid fa-expand" title="Open all Entries"></div>
|
||||
<div id="CloseAllWIEntries" class="menu_button fa-solid fa-compress" title="Close all Entries"></div>
|
||||
<div id="world_popup_new" class="menu_button fa-solid fa-plus" title="New Entry"></div>
|
||||
<div class="flex-container">
|
||||
<form id="form_world_import" action="javascript:void(null);" method="post" enctype="multipart/form-data">
|
||||
<input type="file" id="world_import_file" accept=".json" name="avatar" hidden>
|
||||
</form>
|
||||
<form id="form_rename_world" action="javascript:void(null);" method="post" enctype="multipart/form-data">
|
||||
<input id="world_popup_name" name="world_popup_name" class="text_pole" maxlength="99" size="32" value="" autocomplete="off">
|
||||
<label for="world_popup_name_button" class="menu_button fa-solid fa-pencil faSmallFontSquareFix" title="Rename World Info">
|
||||
<input id="world_popup_name_button" type="submit" value="">
|
||||
</label>
|
||||
<div id="world_popup_bottom_holder" class="flex-container">
|
||||
<div id="world_popup_new" class="menu_button fa-solid fa-plus margin0 faSmallFontSquareFix" title="New Entry"></div>
|
||||
<div id="world_popup_export" class="menu_button fa-solid fa-file-export margin0 faSmallFontSquareFix" title="Export World Info"></div>
|
||||
<div id="world_popup_delete" class="menu_button fa-solid fa-trash-can redWarningBG margin0 faSmallFontSquareFix" title="Delete World Info"></div>
|
||||
</div>
|
||||
<div id="world_create_button" class="menu_button fa-solid fa-globe fa-fw" title="Create"></div>
|
||||
<div id="world_import_button" class="menu_button fa-solid fa-file-import fa-fw" title="Import World Info"></div>
|
||||
<div id="world_popup_export" class="menu_button fa-solid fa-file-export margin0 fa-fw" title="Export World Info"></div>
|
||||
<div id="world_popup_delete" class="menu_button fa-solid fa-trash-can redWarningBG margin0 fa-fw" title="Delete World Info"></div>
|
||||
<span> Editing:</span>
|
||||
<select id="world_editor_select" class="margin0"></select>
|
||||
<div id="world_popup_name_button" class="menu_button fa-solid fa-i-cursor fa-fw" title="Rename World Info"></div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
@ -23,9 +23,7 @@ import {
|
||||
world_info_depth,
|
||||
world_info,
|
||||
getWorldInfoPrompt,
|
||||
selectImportedWorldInfo,
|
||||
setWorldInfoSettings,
|
||||
deleteWorldInfo,
|
||||
world_info_recursive,
|
||||
world_info_case_sensitive,
|
||||
world_info_match_whole_words,
|
||||
@ -4958,12 +4956,10 @@ function callPopup(text, type, inputValue = '') {
|
||||
$("#dialogue_popup_ok").text("Ok");
|
||||
$("#dialogue_popup_cancel").css("display", "none");
|
||||
break;
|
||||
case "world_imported":
|
||||
case "new_chat":
|
||||
case "confirm":
|
||||
$("#dialogue_popup_ok").text("Yes");
|
||||
break;
|
||||
case "del_world":
|
||||
case "del_group":
|
||||
case "rename_chat":
|
||||
case "del_chat":
|
||||
@ -6347,15 +6343,9 @@ $(document).ready(function () {
|
||||
},
|
||||
});
|
||||
}
|
||||
if (popup_type === "world_imported") {
|
||||
selectImportedWorldInfo();
|
||||
}
|
||||
if (popup_type == "alternate_greeting" && menu_type !== "create") {
|
||||
createOrEditCharacter();
|
||||
}
|
||||
if (popup_type === "del_world" && world_info) {
|
||||
deleteWorldInfo(world_info);
|
||||
}
|
||||
if (popup_type === "del_group") {
|
||||
const groupId = $("#dialogue_popup").data("group_id");
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { saveSettings, callPopup, substituteParams, getTokenCount, getRequestHeaders, chat_metadata, this_chid, characters } from "../script.js";
|
||||
import { download, debounce, delay, initScrollHeight, resetScrollHeight } from "./utils.js";
|
||||
import { download, debounce, initScrollHeight, resetScrollHeight } from "./utils.js";
|
||||
import { getContext } from "./extensions.js";
|
||||
import { metadata_keys, shouldWIAddPrompt } from "./extensions/floating-prompt/index.js";
|
||||
|
||||
@ -12,10 +12,8 @@ export {
|
||||
world_info_match_whole_words,
|
||||
world_info_character_strategy,
|
||||
world_names,
|
||||
imported_world_name,
|
||||
checkWorldInfo,
|
||||
deleteWorldInfo,
|
||||
selectImportedWorldInfo,
|
||||
setWorldInfoSettings,
|
||||
getWorldInfoPrompt,
|
||||
}
|
||||
@ -30,12 +28,10 @@ let world_info = null;
|
||||
let world_names;
|
||||
let world_info_depth = 2;
|
||||
let world_info_budget = 128;
|
||||
let is_world_edit_open = false;
|
||||
let world_info_recursive = false;
|
||||
let world_info_case_sensitive = false;
|
||||
let world_info_match_whole_words = false;
|
||||
let world_info_character_strategy = world_info_insertion_strategy.evenly;
|
||||
let imported_world_name = "";
|
||||
const saveWorldDebounced = debounce(async (name, data) => await _save(name, data), 1000);
|
||||
const saveSettingsDebounced = debounce(() => saveSettings(), 1000);
|
||||
|
||||
@ -95,23 +91,23 @@ function setWorldInfoSettings(settings, data) {
|
||||
|
||||
world_names.forEach((item, i) => {
|
||||
$("#world_info").append(`<option value='${i}'>${item}</option>`);
|
||||
$("#world_editor_select").append(`<option value='${i}'>${item}</option>`);
|
||||
// preselect world if saved
|
||||
if (item == world_info) {
|
||||
$("#world_info").val(i).change();
|
||||
$("#world_info").val(i).trigger('change');
|
||||
}
|
||||
});
|
||||
|
||||
$("#world_editor_select").trigger("change");
|
||||
}
|
||||
|
||||
// World Info Editor
|
||||
async function showWorldEditor(name) {
|
||||
if (!name) {
|
||||
toastr.warning("Select a world info first!");
|
||||
hideWorldEditor();
|
||||
return;
|
||||
}
|
||||
|
||||
is_world_edit_open = true;
|
||||
$("#world_popup_name").val(name);
|
||||
$("#world_popup").css("display", "flex");
|
||||
const wiData = await loadWorldInfoData(name);
|
||||
displayWorldEntries(name, wiData);
|
||||
}
|
||||
@ -146,29 +142,36 @@ async function updateWorldInfoList(importedWorldName) {
|
||||
var data = await result.json();
|
||||
world_names = data.world_names?.length ? data.world_names : [];
|
||||
$("#world_info").find('option[value!="None"]').remove();
|
||||
$("#world_editor_select").empty();
|
||||
|
||||
world_names.forEach((item, i) => {
|
||||
$("#world_info").append(`<option value='${i}'>${item}</option>`);
|
||||
$("#world_editor_select").append(`<option value='${i}'>${item}</option>`);
|
||||
});
|
||||
|
||||
if (importedWorldName) {
|
||||
const indexOf = world_names.indexOf(world_info);
|
||||
$("#world_info").val(indexOf);
|
||||
|
||||
callPopup("<h3>World imported successfully! Select it now?</h3>", "world_imported");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function hideWorldEditor() {
|
||||
is_world_edit_open = false;
|
||||
$("#world_popup").css("display", "none");
|
||||
displayWorldEntries(null, null);
|
||||
}
|
||||
|
||||
function nullWorldInfo() {
|
||||
toastr.info("Create or import a new World Info file first.", "World Info is not set", { timeOut: 10000, preventDuplicates: true });
|
||||
}
|
||||
|
||||
function displayWorldEntries(name, data) {
|
||||
$("#world_popup_entries_list").empty();
|
||||
|
||||
if (!data || !("entries" in data)) {
|
||||
$("#world_popup_new").off('click').on('click', nullWorldInfo);
|
||||
$("#world_popup_name_button").off('click').on('click', nullWorldInfo);
|
||||
$("#world_popup_export").off('click').on('click', nullWorldInfo);
|
||||
$("#world_popup_delete").off('click').on('click', nullWorldInfo);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -181,8 +184,8 @@ function displayWorldEntries(name, data) {
|
||||
createWorldInfoEntry(name, data);
|
||||
});
|
||||
|
||||
$("#world_popup_name_button").off('click').on('click', () => {
|
||||
renameWorldInfo(name, data);
|
||||
$("#world_popup_name_button").off('click').on('click', async () => {
|
||||
await renameWorldInfo(name, data);
|
||||
});
|
||||
|
||||
$("#world_popup_export").off('click').on('click', () => {
|
||||
@ -192,6 +195,16 @@ function displayWorldEntries(name, data) {
|
||||
download(jsonValue, fileName, "application/json");
|
||||
}
|
||||
});
|
||||
|
||||
$("#world_popup_delete").off('click').on('click', async () => {
|
||||
const confirmation = await callPopup(`<h3>Delete the World/Lorebook: "${name}"?</h3>This action is irreversible!`, "confirm");
|
||||
|
||||
if (!confirmation) {
|
||||
return;
|
||||
}
|
||||
|
||||
await deleteWorldInfo(name, world_info);
|
||||
});
|
||||
}
|
||||
|
||||
function appendWorldEntry(name, data, entry) {
|
||||
@ -457,18 +470,31 @@ async function saveWorldInfo(name, data, immediately) {
|
||||
|
||||
async function renameWorldInfo(name, data) {
|
||||
const oldName = name;
|
||||
const newName = $("#world_popup_name").val().trim();
|
||||
const newName = await callPopup("<h3>Rename World Info</h3>Enter a new name:", 'input', oldName);
|
||||
|
||||
if (oldName === newName || !newName) {
|
||||
console.debug("World info rename cancelled");
|
||||
return;
|
||||
}
|
||||
|
||||
if (oldName == world_info) {
|
||||
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;
|
||||
}
|
||||
|
||||
await saveWorldInfo(newName, data, true);
|
||||
await deleteWorldInfo(oldName, newName);
|
||||
await deleteWorldInfo(oldName, selectNewName);
|
||||
|
||||
const selectedIndex = world_names.indexOf(newName);
|
||||
if (selectedIndex !== -1) {
|
||||
$('#world_editor_select').val(selectedIndex).trigger('change');
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteWorldInfo(worldInfoName, selectWorldName) {
|
||||
@ -492,7 +518,7 @@ async function deleteWorldInfo(worldInfoName, selectWorldName) {
|
||||
$("#world_info").val("None").trigger('change');
|
||||
}
|
||||
|
||||
hideWorldEditor();
|
||||
$('#world_editor_select').trigger('change');
|
||||
}
|
||||
}
|
||||
|
||||
@ -525,9 +551,8 @@ function getFreeWorldName() {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
async function createNewWorldInfo() {
|
||||
async function createNewWorldInfo(worldInfoName) {
|
||||
const worldInfoTemplate = { entries: {} };
|
||||
const worldInfoName = getFreeWorldName();
|
||||
|
||||
if (!worldInfoName) {
|
||||
return;
|
||||
@ -540,8 +565,10 @@ async function createNewWorldInfo() {
|
||||
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("None").trigger('change');
|
||||
hideWorldEditor();
|
||||
}
|
||||
}
|
||||
|
||||
@ -629,7 +656,7 @@ async function getSortedEntries() {
|
||||
|
||||
async function checkWorldInfo(chat) {
|
||||
const context = getContext();
|
||||
const messagesToLookBack = world_info_depth * 2;
|
||||
const messagesToLookBack = world_info_depth * 2 || 1;
|
||||
let textToScan = transformString(chat.slice(0, messagesToLookBack).join(""));
|
||||
let worldInfoBefore = "";
|
||||
let worldInfoAfter = "";
|
||||
@ -753,21 +780,8 @@ function matchKeys(haystack, needle) {
|
||||
return false;
|
||||
}
|
||||
|
||||
function selectImportedWorldInfo() {
|
||||
if (!imported_world_name) {
|
||||
return;
|
||||
}
|
||||
|
||||
world_names.forEach((item, i) => {
|
||||
if (item === imported_world_name) {
|
||||
$("#world_info").val(i).change();
|
||||
}
|
||||
});
|
||||
imported_world_name = "";
|
||||
}
|
||||
|
||||
jQuery(() => {
|
||||
$("#world_info").change(async function () {
|
||||
$("#world_info").on('change', async function () {
|
||||
const selectedWorld = $("#world_info").find(":selected").val();
|
||||
world_info = null;
|
||||
|
||||
@ -776,14 +790,12 @@ jQuery(() => {
|
||||
world_info = !isNaN(worldIndex) ? world_names[worldIndex] : null;
|
||||
}
|
||||
|
||||
if (selectedWorld === "None") { hideWorldEditor(); }
|
||||
if (is_world_edit_open && selectedWorld !== "None") { showWorldEditor(world_info) };
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
//**************************WORLD INFO IMPORT EXPORT*************************//
|
||||
$("#world_import_button").click(function () {
|
||||
$("#world_import_file").click();
|
||||
$("#world_import_button").on('click', function () {
|
||||
$("#world_import_file").trigger('click');
|
||||
});
|
||||
|
||||
$("#world_import_file").on("change", function (e) {
|
||||
@ -808,10 +820,16 @@ jQuery(() => {
|
||||
cache: false,
|
||||
contentType: false,
|
||||
processData: false,
|
||||
success: function (data) {
|
||||
success: async function (data) {
|
||||
if (data.name) {
|
||||
imported_world_name = data.name;
|
||||
updateWorldInfoList(imported_world_name);
|
||||
await updateWorldInfoList(data.name);
|
||||
|
||||
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) => { },
|
||||
@ -821,26 +839,25 @@ jQuery(() => {
|
||||
$("#form_world_import").trigger("reset");
|
||||
});
|
||||
|
||||
$("#world_info_edit_button").click(() => {
|
||||
is_world_edit_open ? hideWorldEditor() : showWorldEditor(world_info);
|
||||
});
|
||||
|
||||
$("#world_popup_delete").click(() => {
|
||||
callPopup("<h3>Delete the World Info?</h3>", "del_world");
|
||||
});
|
||||
|
||||
$("#world_cross").click(() => {
|
||||
hideWorldEditor();
|
||||
});
|
||||
|
||||
$("#world_create_button").on('click', async () => {
|
||||
const confirm = await callPopup("<h3>Create a new World Info?</h3>", "confirm");
|
||||
const tempName = getFreeWorldName();
|
||||
const finalName = await callPopup("<h3>Create a new World Info?</h3>Enter a name for the new file:", "input", tempName);
|
||||
|
||||
if (confirm) {
|
||||
await createNewWorldInfo();
|
||||
if (finalName) {
|
||||
await createNewWorldInfo(finalName);
|
||||
}
|
||||
});
|
||||
|
||||
$("#world_editor_select").on('change', async () => {
|
||||
const selectedIndex = $("#world_editor_select").find(":selected").val();
|
||||
const worldName = world_names[selectedIndex];
|
||||
showWorldEditor(worldName);
|
||||
});
|
||||
|
||||
$(document).on("input", "#world_info_depth", function () {
|
||||
world_info_depth = Number($(this).val());
|
||||
$("#world_info_depth_counter").text($(this).val());
|
||||
|
@ -1900,7 +1900,6 @@ grammarly-extension {
|
||||
}
|
||||
|
||||
#world_popup {
|
||||
display: none;
|
||||
min-height: 100px;
|
||||
min-width: 100px;
|
||||
left: 0;
|
||||
@ -1919,11 +1918,6 @@ grammarly-extension {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
|
||||
#world_popup_name_button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#world_popup_bottom_holder {
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
@ -4458,6 +4452,10 @@ body.waifuMode #avatar_zoom_popup {
|
||||
top: 40px;
|
||||
}
|
||||
|
||||
.wi-settings {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#character_popup,
|
||||
#world_popup {
|
||||
overflow-y: auto;
|
||||
|
Reference in New Issue
Block a user