diff --git a/public/script.js b/public/script.js
index 72bc8e4f2..bfe9fe57d 100644
--- a/public/script.js
+++ b/public/script.js
@@ -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() {
diff --git a/public/scripts/group-chats.js b/public/scripts/group-chats.js
index 22bca164e..2d4f2bbbf 100644
--- a/public/scripts/group-chats.js
+++ b/public/scripts/group-chats.js
@@ -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);
diff --git a/public/scripts/world-info.js b/public/scripts/world-info.js
index 77fdfef14..4ee3f8958 100644
--- a/public/scripts/world-info.js
+++ b/public/scripts/world-info.js
@@ -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') {
diff --git a/public/style.css b/public/style.css
index 9fb4edf53..b011b344c 100644
--- a/public/style.css
+++ b/public/style.css
@@ -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;
}
diff --git a/server.js b/server.js
index 6b30399dc..53cbf9a9d 100644
--- a/server.js
+++ b/server.js
@@ -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');