mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2024-12-23 23:55:27 +01:00
#1226 Add chat-bound lorebooks
This commit is contained in:
parent
8a3547ecd0
commit
6a1b230c7e
@ -3428,6 +3428,7 @@
|
||||
<input type="hidden" id="fav_checkbox" name="fav" />
|
||||
<div id="advanced_div" class="menu_button fa-solid fa-book " title="Advanced Definitions" data-i18n="[title]Advanced Definition"></div>
|
||||
<div id="world_button" class="menu_button fa-solid fa-globe" title="Character Lore" data-i18n="[title]Character Lore"></div>
|
||||
<div class="chat_lorebook_button menu_button fa-solid fa-passport" title="Chat Lore" data-i18n="[title]Chat Lore"></div>
|
||||
<div id="export_button" class="menu_button fa-solid fa-file-export " title="Export and Download" data-i18n="[title]Export and Download"></div>
|
||||
<!-- <div id="set_chat_scenario" class="menu_button fa-solid fa-scroll" title="Set a chat scenario override"></div> -->
|
||||
<!-- <div id="set_character_world" class="menu_button fa-solid fa-globe" title="Set a character World Info / Lorebook"></div> -->
|
||||
@ -3543,8 +3544,11 @@
|
||||
<div class="fa-solid fa-circle-chevron-down inline-drawer-icon down"></div>
|
||||
</div>
|
||||
<div class="inline-drawer-content">
|
||||
<div name="group-metadata-controls" class="marginTopBot5">
|
||||
<input id="rm_group_chat_name" class="text_pole wide100p" type="text" name="chat_name" data-i18n="[placeholder]Chat Name (Optional)" placeholder="Chat Name (Optional)" maxlength="100" />
|
||||
<div id="group-metadata-controls" class="marginTopBot5">
|
||||
<div class="flex-container wide100p">
|
||||
<input id="rm_group_chat_name" class="text_pole flex1" type="text" name="chat_name" data-i18n="[placeholder]Chat Name (Optional)" placeholder="Chat Name (Optional)" maxlength="100" />
|
||||
<div class="chat_lorebook_button menu_button fa-solid fa-passport" title="Chat Lore" data-i18n="[title]Chat Lore"></div>
|
||||
</div>
|
||||
<div id="group_tags_div" class="wide100p">
|
||||
<div class="tag_controls">
|
||||
<input id="groupTagInput" class="text_pole tag_input flex1 margin0" data-i18n="[placeholder]Search / Create Tags" placeholder="Search / Create tags" maxlength="25" />
|
||||
@ -3896,6 +3900,25 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="chat_world_template" class="template_element">
|
||||
<div class="chat_world range-block flexFlowColumn flex-container">
|
||||
<div class="range-block-title">
|
||||
<h4 data-i18n="Chat Lorebook">Chat Lorebook for <span class="chat_name"></span></h4>
|
||||
</div>
|
||||
<div class="range-block-counter justifyLeft flex-container flexFlowColumn margin-bot-10px">
|
||||
<span data-i18n="A selected World Info will be bound to this chat.">
|
||||
A selected World Info will be bound to this chat. When generating an AI reply,
|
||||
it will be combined with the entries from global and character lorebooks.
|
||||
</span>
|
||||
</div>
|
||||
<div class="range-block-range wide100p">
|
||||
<select class="chat_world_info_selector wide100p">
|
||||
<option value="">--- None ---</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="character_world_template" class="template_element">
|
||||
<div class="character_world range-block flexFlowColumn flex-container">
|
||||
<div class="range-block-title">
|
||||
|
@ -5729,6 +5729,7 @@ export function select_selected_character(chid) {
|
||||
checkEmbeddedWorld(chid);
|
||||
|
||||
$("#form_create").attr("actiontype", "editcharacter");
|
||||
$('.form_create_bottom_buttons_block .chat_lorebook_button').show();
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
@ -5787,6 +5788,7 @@ function select_rm_create() {
|
||||
checkEmbeddedWorld();
|
||||
|
||||
$("#form_create").attr("actiontype", "createcharacter");
|
||||
$('.form_create_bottom_buttons_block .chat_lorebook_button').hide();
|
||||
}
|
||||
|
||||
function select_rm_characters() {
|
||||
|
@ -1110,6 +1110,7 @@ function select_group_chats(groupId, skipAnimation) {
|
||||
$("#rm_group_submit").hide();
|
||||
$("#rm_group_delete").show();
|
||||
$("#rm_group_scenario").show();
|
||||
$('#group-metadata-controls .chat_lorebook_button').removeClass('disabled').prop('disabled', false);
|
||||
} else {
|
||||
$("#rm_group_submit").show();
|
||||
if ($("#groupAddMemberListToggle .inline-drawer-content").css('display') !== 'block') {
|
||||
@ -1117,6 +1118,7 @@ function select_group_chats(groupId, skipAnimation) {
|
||||
}
|
||||
$("#rm_group_delete").hide();
|
||||
$("#rm_group_scenario").hide();
|
||||
$('#group-metadata-controls .chat_lorebook_button').addClass('disabled').prop('disabled', true);
|
||||
}
|
||||
|
||||
updateFavButtonState(group?.fav ?? false);
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { saveSettings, callPopup, substituteParams, getRequestHeaders, chat_metadata, this_chid, characters, saveCharacterDebounced, menu_type, eventSource, event_types, getExtensionPrompt, MAX_INJECTION_DEPTH, extension_prompt_types, getExtensionPromptByName } from "../script.js";
|
||||
import { saveSettings, callPopup, substituteParams, getRequestHeaders, chat_metadata, this_chid, characters, saveCharacterDebounced, menu_type, eventSource, event_types, getExtensionPrompt, MAX_INJECTION_DEPTH, extension_prompt_types, getExtensionPromptByName, saveMetadata, getCurrentChatId } from "../script.js";
|
||||
import { download, debounce, initScrollHeight, resetScrollHeight, parseJsonFile, extractDataFromPng, getFileBuffer, getCharaFilename, getSortableDelay, escapeRegex, PAGINATION_TEMPLATE, navigation_option, waitUntilCondition } from "./utils.js";
|
||||
import { extension_settings, getContext } from "./extensions.js";
|
||||
import { NOTE_MODULE_NAME, metadata_keys, shouldWIAddPrompt } from "./authors-note.js";
|
||||
@ -53,6 +53,7 @@ let updateEditor = (navigation) => { navigation; };
|
||||
// Do not optimize. updateEditor is a function that is updated by the displayWorldEntries with new data.
|
||||
const worldInfoFilter = new FilterHelper(() => updateEditor());
|
||||
const SORT_ORDER_KEY = 'world_info_sort_order';
|
||||
const METADATA_KEY = 'world_info';
|
||||
|
||||
const InputWidthReference = $("#WIInputWidthReference");
|
||||
|
||||
@ -167,6 +168,11 @@ function setWorldInfoSettings(settings, data) {
|
||||
|
||||
$('#world_info_sort_order').val(localStorage.getItem(SORT_ORDER_KEY) || '0');
|
||||
$("#world_editor_select").trigger("change");
|
||||
|
||||
eventSource.on(event_types.CHAT_CHANGED, () => {
|
||||
const hasWorldInfo = !!chat_metadata[METADATA_KEY] && world_names.includes(chat_metadata[METADATA_KEY]);
|
||||
$('.chat_lorebook_button').toggleClass('world_set', hasWorldInfo);
|
||||
});
|
||||
}
|
||||
|
||||
// World Info Editor
|
||||
@ -1301,10 +1307,26 @@ async function getGlobalLore() {
|
||||
return entries;
|
||||
}
|
||||
|
||||
async function getChatLore() {
|
||||
const chatWorld = chat_metadata[METADATA_KEY];
|
||||
|
||||
if (!chatWorld) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const data = await loadWorldInfoData(chatWorld);
|
||||
const entries = data ? Object.keys(data.entries).map((x) => data.entries[x]) : [];
|
||||
|
||||
console.debug(`Chat lore has ${entries.length} entries`);
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
async function getSortedEntries() {
|
||||
try {
|
||||
const globalLore = await getGlobalLore();
|
||||
const characterLore = await getCharacterLore();
|
||||
const chatLore = await getChatLore();
|
||||
|
||||
let entries;
|
||||
|
||||
@ -1327,6 +1349,9 @@ async function getSortedEntries() {
|
||||
break;
|
||||
}
|
||||
|
||||
// Chat lore always goes first
|
||||
entries = [...chatLore.sort(sortFn), ...entries];
|
||||
|
||||
console.debug(`Sorted ${entries.length} world lore entries using strategy ${world_info_character_strategy}`);
|
||||
|
||||
// Need to deep clone the entries to avoid modifying the cached data
|
||||
@ -1911,6 +1936,39 @@ export async function importWorldInfo(file) {
|
||||
});
|
||||
}
|
||||
|
||||
function assignLorebookToChat() {
|
||||
const selectedName = chat_metadata[METADATA_KEY];
|
||||
const template = $('#chat_world_template .chat_world').clone();
|
||||
|
||||
const worldSelect = template.find('select');
|
||||
const chatName = template.find('.chat_name');
|
||||
chatName.text(getCurrentChatId());
|
||||
|
||||
for (const worldName of world_names) {
|
||||
const option = document.createElement('option');
|
||||
option.value = worldName;
|
||||
option.innerText = worldName;
|
||||
option.selected = selectedName === worldName;
|
||||
worldSelect.append(option);
|
||||
}
|
||||
|
||||
worldSelect.on('change', function () {
|
||||
const worldName = $(this).val();
|
||||
|
||||
if (worldName) {
|
||||
chat_metadata[METADATA_KEY] = worldName;
|
||||
$('.chat_lorebook_button').addClass('world_set');
|
||||
} else {
|
||||
delete chat_metadata[METADATA_KEY];
|
||||
$('.chat_lorebook_button').removeClass('world_set');
|
||||
}
|
||||
|
||||
saveMetadata();
|
||||
});
|
||||
|
||||
callPopup(template, 'text');
|
||||
}
|
||||
|
||||
jQuery(() => {
|
||||
|
||||
$(document).ready(function () {
|
||||
@ -2051,6 +2109,8 @@ jQuery(() => {
|
||||
updateEditor(navigation_option.none);
|
||||
})
|
||||
|
||||
$(document).on('click', '.chat_lorebook_button', assignLorebookToChat);
|
||||
|
||||
// Not needed on mobile
|
||||
const deviceInfo = getDeviceInfo();
|
||||
if (deviceInfo && deviceInfo.device.type === 'desktop') {
|
||||
|
@ -1316,7 +1316,7 @@ select option:not(:checked) {
|
||||
}
|
||||
|
||||
.menu_button.disabled {
|
||||
filter: brightness(50%);
|
||||
filter: brightness(75%) grayscale(1);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
@ -1911,10 +1911,10 @@ grammarly-extension {
|
||||
font-weight: bold;
|
||||
padding: 5px;
|
||||
margin: 0;
|
||||
height: 32px;
|
||||
height: 26px;
|
||||
filter: grayscale(0.5);
|
||||
text-align: center;
|
||||
font-size: 20px;
|
||||
font-size: 17px;
|
||||
aspect-ratio: 1 / 1;
|
||||
}
|
||||
|
||||
@ -2307,7 +2307,7 @@ input[type="range"]::-webkit-slider-thumb {
|
||||
|
||||
#char-management-dropdown,
|
||||
#tagInput {
|
||||
height: 32px;
|
||||
height: 26px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
|
@ -1811,15 +1811,18 @@ function convertWorldInfoToCharacterBook(name, entries) {
|
||||
}
|
||||
|
||||
function readWorldInfoFile(worldInfoName) {
|
||||
const dummyObject = { entries: {} };
|
||||
|
||||
if (!worldInfoName) {
|
||||
return { entries: {} };
|
||||
return dummyObject;
|
||||
}
|
||||
|
||||
const filename = `${worldInfoName}.json`;
|
||||
const pathToWorldInfo = path.join(DIRECTORIES.worlds, filename);
|
||||
|
||||
if (!fs.existsSync(pathToWorldInfo)) {
|
||||
throw new Error(`World info file ${filename} doesn't exist.`);
|
||||
console.log(`World info file ${filename} doesn't exist.`);
|
||||
return dummyObject;
|
||||
}
|
||||
|
||||
const worldInfoText = fs.readFileSync(pathToWorldInfo, 'utf8');
|
||||
|
Loading…
Reference in New Issue
Block a user