mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
#21 Add world info editor
This commit is contained in:
@@ -69,6 +69,7 @@
|
||||
|
||||
var timerSaveEdit;
|
||||
var timerKoboldSync;
|
||||
var timerWorldSave;
|
||||
var durationSaveEdit = 200;
|
||||
//animation right menu
|
||||
var animation_rm_duration = 200;
|
||||
@@ -109,6 +110,7 @@
|
||||
var kobold_world_synced = false;
|
||||
var kobold_sync_failed = false;
|
||||
var kobold_is_united = false;
|
||||
var kobold_world_data = null;
|
||||
var imported_world_name = '';
|
||||
var max_context = 2048;//2048;
|
||||
var rep_pen = 1;
|
||||
@@ -2133,11 +2135,16 @@
|
||||
|
||||
// If we can reach Kobold's new ui, then it should be United branch
|
||||
kobold_is_united = false;
|
||||
const kobold_united_ui2 = api_server.replace('/api', '/new_ui');
|
||||
const response = await fetch(kobold_united_ui2, { method: 'HEAD'});
|
||||
try {
|
||||
const kobold_united_ui2 = api_server.replace('/api', '/new_ui');
|
||||
const response = await fetch(kobold_united_ui2, { method: 'HEAD'});
|
||||
|
||||
if (response.ok && response.status == 200) {
|
||||
kobold_is_united = true;
|
||||
if (response.ok && response.status == 200) {
|
||||
kobold_is_united = true;
|
||||
}
|
||||
}
|
||||
catch {
|
||||
// empty catch
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2927,7 +2934,7 @@
|
||||
|
||||
jQuery.ajax({
|
||||
type: 'POST',
|
||||
url: '/importworld',
|
||||
url: '/importworldinfo',
|
||||
data: formData,
|
||||
beforeSend: () => {},
|
||||
cache: false,
|
||||
@@ -2964,7 +2971,7 @@
|
||||
|
||||
if (importedWorldName) {
|
||||
const indexOf = koboldai_world_names.indexOf(kobold_world);
|
||||
$(`#world_info`).val(indexOf);
|
||||
$('#world_info').val(indexOf);
|
||||
|
||||
popup_type = 'world_imported';
|
||||
callPopup('<h3>World imported successfully! Select it now?</h3>');
|
||||
@@ -2983,7 +2990,7 @@
|
||||
// World Info Editor
|
||||
async function showWorldEditor() {
|
||||
is_world_edit_open = true;
|
||||
$('#world_text_content').val('');
|
||||
$('#world_popup_name').val(kobold_world);
|
||||
$('#world_popup').css('display', 'flex');
|
||||
|
||||
if (kobold_world) {
|
||||
@@ -2994,8 +3001,8 @@
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const worldInfoData = await response.text();
|
||||
$('#world_text_content').val(worldInfoData);
|
||||
kobold_world_data = await response.json();
|
||||
displayWorldEntries(kobold_world_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3003,10 +3010,209 @@
|
||||
function hideWorldEditor() {
|
||||
is_world_edit_open = false;
|
||||
$('#world_popup').css('display', 'none');
|
||||
$('#world_text_content').val('');
|
||||
syncKoboldWorldInfo(true);
|
||||
}
|
||||
|
||||
async function deleteWorldInfo(worldInfoName) {
|
||||
function displayWorldEntries(data) {
|
||||
$('#world_popup_entries_list').empty();
|
||||
|
||||
if (!data || !('entries' in data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const entryUid in data.entries) {
|
||||
const entry = data.entries[entryUid];
|
||||
appendWorldEntry(entry);
|
||||
}
|
||||
}
|
||||
|
||||
function appendWorldEntry(entry) {
|
||||
const template = $('#entry_edit_template .world_entry').clone();
|
||||
template.data('uid', entry.uid);
|
||||
|
||||
// key
|
||||
const keyInput = template.find('input[name="key"]');
|
||||
keyInput.data('uid', entry.uid);
|
||||
keyInput.on('input', function () {
|
||||
const uid = $(this).data('uid');
|
||||
const value = $(this).val();
|
||||
kobold_world_data.entries[uid].key = value.split(',').map(x => x.trim()).filter(x => x);
|
||||
saveWorldInfo();
|
||||
});
|
||||
keyInput.val(entry.key.join(',')).trigger('input');
|
||||
|
||||
// keysecondary
|
||||
const keySecondaryInput = template.find('input[name="keysecondary"]');
|
||||
keySecondaryInput.data('uid', entry.uid);
|
||||
keySecondaryInput.on('input', function() {
|
||||
const uid = $(this).data('uid');
|
||||
const value = $(this).val();
|
||||
kobold_world_data.entries[uid].keysecondary = value.split(',').map(x => x.trim()).filter(x => x);
|
||||
saveWorldInfo();
|
||||
});
|
||||
keySecondaryInput.val(entry.keysecondary.join(',')).trigger('input');
|
||||
|
||||
// comment
|
||||
const commentInput = template.find('input[name="comment"]');
|
||||
commentInput.data('uid', entry.uid);
|
||||
commentInput.on('input', function() {
|
||||
const uid = $(this).data('uid');
|
||||
const value = $(this).val();
|
||||
kobold_world_data.entries[uid].comment = value;
|
||||
saveWorldInfo();
|
||||
});
|
||||
commentInput.val(entry.comment).trigger('input');
|
||||
|
||||
// content
|
||||
const contentInput = template.find('textarea[name="content"]');
|
||||
contentInput.data('uid', entry.uid);
|
||||
contentInput.on('input', function() {
|
||||
const uid = $(this).data('uid');
|
||||
const value = $(this).val();
|
||||
kobold_world_data.entries[uid].content = value;
|
||||
saveWorldInfo();
|
||||
|
||||
// count tokens
|
||||
const numberOfTokens = encode(value).length;
|
||||
$(this).closest('.world_entry').find('.world_entry_form_token_counter').html(numberOfTokens);
|
||||
});
|
||||
contentInput.val(entry.content).trigger('input');
|
||||
|
||||
// selective
|
||||
const selectiveInput = template.find('input[name="selective"]')
|
||||
selectiveInput.data('uid', entry.uid);
|
||||
selectiveInput.on('input', function() {
|
||||
const uid = $(this).data('uid');
|
||||
const value = $(this).prop('checked');
|
||||
kobold_world_data.entries[uid].selective = value;
|
||||
saveWorldInfo();
|
||||
});
|
||||
selectiveInput.prop('checked', entry.selective).trigger('input');
|
||||
selectiveInput.siblings('.checkbox_fancy').click(function() {
|
||||
$(this).siblings('input').click();
|
||||
});
|
||||
|
||||
|
||||
// constant
|
||||
const constantInput = template.find('input[name="constant"]')
|
||||
constantInput.data('uid', entry.uid);
|
||||
constantInput.on('input', function() {
|
||||
const uid = $(this).data('uid');
|
||||
const value = $(this).prop('checked');
|
||||
kobold_world_data.entries[uid].constant = value;
|
||||
saveWorldInfo();
|
||||
});
|
||||
constantInput.prop('checked', entry.constant).trigger('input');
|
||||
constantInput.siblings('.checkbox_fancy').click(function() {
|
||||
$(this).siblings('input').click();
|
||||
});
|
||||
|
||||
// display uid
|
||||
template.find('.world_entry_form_uid_value').html(entry.uid);
|
||||
|
||||
// delete button
|
||||
const deleteButton = template.find('input.delete_entry_button');
|
||||
deleteButton.data('uid', entry.uid);
|
||||
deleteButton.on('click', function() {
|
||||
const uid = $(this).data('uid');
|
||||
deleteWorldInfoEntry(uid);
|
||||
$(this).closest('.world_entry').remove();
|
||||
saveWorldInfo();
|
||||
});
|
||||
|
||||
template.appendTo('#world_popup_entries_list');
|
||||
return template;
|
||||
}
|
||||
|
||||
async function deleteWorldInfoEntry(uid) {
|
||||
if (!kobold_world_data || !('entries' in kobold_world_data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
delete kobold_world_data.entries[uid];
|
||||
|
||||
if ('folders' in kobold_world_data) {
|
||||
for (const folderName in kobold_world_data.folders) {
|
||||
const folder = kobold_world_data.folders[folderName]
|
||||
const index = folder.indexOf(Number(uid));
|
||||
|
||||
if (index !== -1) {
|
||||
folder.splice(index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function createWorldInfoEntry() {
|
||||
const newEntryTemplate = {
|
||||
key: [],
|
||||
keysecondary: [],
|
||||
comment: '',
|
||||
content: '',
|
||||
constant: false,
|
||||
selective: false,
|
||||
};
|
||||
const newUid = getFreeWorldEntryUid();
|
||||
|
||||
if (!Number.isInteger(newUid)) {
|
||||
console.error("Couldn't assign UID to a new entry");
|
||||
return;
|
||||
}
|
||||
|
||||
const newEntry = { uid: newUid, ...newEntryTemplate };
|
||||
kobold_world_data.entries[newUid] = newEntry;
|
||||
|
||||
if ('folders' in kobold_world_data) {
|
||||
if (kobold_world in kobold_world_data.folders && Array.isArray(kobold_world_data.folders)) {
|
||||
kobold_world_data.folders[kobold_world].push(newUid);
|
||||
} else {
|
||||
kobold_world_data.folders[kobold_world] = [newUid];
|
||||
}
|
||||
}
|
||||
|
||||
const entryTemplate = appendWorldEntry(newEntry);
|
||||
entryTemplate.get(0).scrollIntoView({behavior: 'smooth'});
|
||||
}
|
||||
|
||||
async function saveWorldInfo(immediately) {
|
||||
if (!kobold_world || !kobold_world_data) {
|
||||
return;
|
||||
}
|
||||
|
||||
async function _save() {
|
||||
const response = await fetch("/editworldinfo", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ name: kobold_world, data: kobold_world_data })
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
kobold_world_synced = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (immediately) {
|
||||
return await _save();
|
||||
}
|
||||
|
||||
clearTimeout(timerWorldSave);
|
||||
timerWorldSave = setTimeout(async () => await _save(), durationSaveEdit);
|
||||
}
|
||||
|
||||
async function renameWorldInfo() {
|
||||
const oldName = kobold_world;
|
||||
const newName = $('#world_popup_name').val();
|
||||
|
||||
if (oldName === newName) {
|
||||
return;
|
||||
}
|
||||
|
||||
kobold_world = newName;
|
||||
await saveWorldInfo(true);
|
||||
await deleteWorldInfo(oldName, newName);
|
||||
}
|
||||
|
||||
async function deleteWorldInfo(worldInfoName, selectWorldName) {
|
||||
if (!koboldai_world_names.includes(worldInfoName)) {
|
||||
return;
|
||||
}
|
||||
@@ -3019,18 +3225,42 @@
|
||||
|
||||
if (response.ok) {
|
||||
await updateWorldInfoList();
|
||||
$('#world_info').val('None').change();
|
||||
|
||||
const selectedIndex = koboldai_world_names.indexOf(selectWorldName);
|
||||
if (selectedIndex !== -1) {
|
||||
$('#world_info').val(selectedIndex).change();
|
||||
}
|
||||
else {
|
||||
$('#world_info').val('None').change();
|
||||
}
|
||||
|
||||
hideWorldEditor();
|
||||
}
|
||||
}
|
||||
|
||||
function getFreeWorldEntryUid() {
|
||||
if (!kobold_world_data || !('entries' in kobold_world_data)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const MAX_UID = 1_000_000; // <- should be safe enough :)
|
||||
for (let uid = 0; uid < MAX_UID; uid++) {
|
||||
if (uid in kobold_world_data.entries) {
|
||||
continue;
|
||||
}
|
||||
return uid;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
$('#world_info_edit_button').click(() => {
|
||||
is_world_edit_open ? hideWorldEditor() : showWorldEditor();
|
||||
});
|
||||
|
||||
$('#world_popup_export').click(() => {
|
||||
const jsonValue = $('#world_text_content').val();
|
||||
if (kobold_world && jsonValue) {
|
||||
if (kobold_world && kobold_world_data) {
|
||||
const jsonValue = JSON.stringify(kobold_world_data);
|
||||
const fileName = `${kobold_world}.json`;
|
||||
download(jsonValue, fileName, 'application/json');
|
||||
}
|
||||
@@ -3041,9 +3271,17 @@
|
||||
callPopup('<h3>Delete the World Info?</h3>');
|
||||
});
|
||||
|
||||
$('#world_popup_new').click(() => {
|
||||
createWorldInfoEntry();
|
||||
});
|
||||
|
||||
$('#world_cross').click(() => {
|
||||
hideWorldEditor();
|
||||
});
|
||||
|
||||
$('#world_popup_name_button').click(() => {
|
||||
renameWorldInfo();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<title>Tavern.AI</title>
|
||||
@@ -3105,20 +3343,88 @@
|
||||
<div id="world_popup">
|
||||
<div id="world_popup_text">
|
||||
<img id="world_cross" src="img/cross.png">
|
||||
<div>
|
||||
<!-- Probably needs a logo (probably) -->
|
||||
<!-- <img src="img/book2.png" id="world_logo"> -->
|
||||
<h3>World Info creation</h3>
|
||||
<div id="world_popup_header">
|
||||
<!-- Consider changing logo to something else -->
|
||||
<img src="img/book2.png" id="world_logo">
|
||||
<h3>
|
||||
World Info Editor
|
||||
<span>(<a href="/notes/13" target="_blank">?</a>)</span>
|
||||
</h3>
|
||||
<div class="world_popup_expander"> </div>
|
||||
<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">
|
||||
<input id="world_popup_name_button" type="submit" value="Rename">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Placeholder until actual editor is implemented -->
|
||||
<h4>File content (read-only)</h4>
|
||||
<textarea id="world_text_content" placeholder="Loading..." form="form_create" disabled></textarea>
|
||||
<div id="world_popup_entries_list">
|
||||
</div>
|
||||
|
||||
<div id="world_popup_bottom_holder">
|
||||
<div id="world_popup_new" class="menu_button">New Entry</div>
|
||||
<div class="world_popup_expander"> </div>
|
||||
<div id="world_popup_export" class="menu_button">Export</div>
|
||||
<div id="world_popup_delete" class="menu_button">Delete</div>
|
||||
<div id="world_popup_delete" class="menu_button">Delete World</div>
|
||||
</div>
|
||||
|
||||
<div id="entry_edit_template">
|
||||
<div class="world_entry">
|
||||
<form class="world_entry_form">
|
||||
<div class="world_entry_form_control">
|
||||
<label for="key">
|
||||
<h4>Key</h4>
|
||||
<h5>Comma-separated list of keywords (e.g: foo,bar).</h5>
|
||||
</label>
|
||||
<input class="text_pole" type="text" name="key" placeholder=""/>
|
||||
</div>
|
||||
<div class="world_entry_form_control">
|
||||
<label for="keysecondary">
|
||||
<h4>Secondary Key</h4>
|
||||
<h5>Comma-separated list of additional keywords (e.g: foo,bar).</h5>
|
||||
</label>
|
||||
<input class="text_pole" type="text" name="keysecondary" placeholder=""/>
|
||||
</div>
|
||||
<div class="world_entry_form_control">
|
||||
<label for="comment">
|
||||
<h4>Comment</h4>
|
||||
<h5>Optional comment (doesn't affect the AI).</h5>
|
||||
</label>
|
||||
<input class="text_pole" type="text" name="comment" placeholder=""/>
|
||||
</div>
|
||||
<div class="world_entry_form_control">
|
||||
<label for="content">
|
||||
<h4>Content</h4>
|
||||
<h5>Text that will be inserted to the prompt upon activation.</h5>
|
||||
</label>
|
||||
<textarea class="text_pole" name="content" rows="4" placeholder=""></textarea>
|
||||
</div>
|
||||
<div class="world_entry_form_control world_entry_form_horizontal">
|
||||
<label class="checkbox" for="constant">
|
||||
<input type="checkbox" name="constant" />
|
||||
<span class="checkbox_fancy"></span>
|
||||
<h4>Constant</h4>
|
||||
</label>
|
||||
<label class="checkbox" for="selective">
|
||||
<input type="checkbox" name="selective" />
|
||||
<span class="checkbox_fancy"></span>
|
||||
<h4>Selective</h4>
|
||||
</label>
|
||||
<span class="world_popup_expander"> </span>
|
||||
<h5 class="world_entry_form_uid">
|
||||
UID:
|
||||
|
||||
<span class="world_entry_form_uid_value"></span>
|
||||
</h5>
|
||||
<h5 class="world_entry_form_tokens">
|
||||
Tokens used:
|
||||
|
||||
<span class="world_entry_form_token_counter">0</span>
|
||||
</h5>
|
||||
<input class="delete_entry_button" type="button" value="Delete Entry" />
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
141
public/style.css
141
public/style.css
@@ -1012,7 +1012,6 @@ input[type=button] {
|
||||
height: 83vh;
|
||||
position: absolute;
|
||||
z-index: 2060;
|
||||
background-color: blue;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
left: 0;
|
||||
@@ -1024,11 +1023,6 @@ input[type=button] {
|
||||
border-radius: 1px;
|
||||
}
|
||||
|
||||
#world_text_content {
|
||||
margin: 0;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
#world_popup_bottom_holder {
|
||||
padding: 1rem 0;
|
||||
display: flex;
|
||||
@@ -1040,6 +1034,123 @@ input[type=button] {
|
||||
margin-left: 1rem;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
#entry_edit_template {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.world_entry:not(:last-child)::after {
|
||||
margin-top: 1rem;
|
||||
height: 1px;
|
||||
display: block;
|
||||
width: 100%;
|
||||
content: '';
|
||||
background-image: linear-gradient(270deg, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.75), rgba(0, 0, 0, 0));
|
||||
}
|
||||
|
||||
#world_popup_header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
#form_rename_world {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: 20px;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
#form_rename_world input[type="submit"] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#form_rename_world input:not(:last-child) {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
#world_popup_header h5 {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.world_popup_expander {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
#world_popup_entries_list {
|
||||
flex-grow: 1;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
#world_popup_entries_list:empty {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#world_popup_entries_list:empty::before {
|
||||
content: 'No entries exist. Try creating one!';
|
||||
font-size: 1.5rem;
|
||||
font-weight: bolder;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.world_entry_form_control {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
.world_entry_form_control label {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
|
||||
.world_entry_form_control label h4 {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
.world_entry_form_control label h5 {
|
||||
margin-top: 3px;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
|
||||
.world_entry_form_control textarea {
|
||||
height: auto;
|
||||
width: auto;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.world_entry_form_control.world_entry_form_horizontal {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.world_entry_form_control input[type=button] {
|
||||
opacity: 0.7;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.world_entry_form_horizontal h5 {
|
||||
margin: 0 1rem;
|
||||
}
|
||||
|
||||
.world_entry_form_control .checkbox h4 {
|
||||
margin-left: 0.5rem;
|
||||
margin-top: 0;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.world_entry_form_control .checkbox:not(:first-child) {
|
||||
margin-left: 2rem;
|
||||
}
|
||||
|
||||
#world_cross {
|
||||
@@ -1052,19 +1163,17 @@ input[type=button] {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
#world_popup h5 a {
|
||||
#world_logo {
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
#world_popup h4 a, #world_popup h5 a, #world_popup h3 a {
|
||||
color: #936f4a;
|
||||
}
|
||||
|
||||
#world_popup h5 a:hover {
|
||||
color: #998e6b;
|
||||
}
|
||||
|
||||
#world_popup h4 a {
|
||||
color: #936f4a;
|
||||
}
|
||||
|
||||
#world_popup h4 a:hover {
|
||||
#world_popup h5 a:hover, #world_popup h4 a:hover, #world_popup h4 a:hover a {
|
||||
color: #998e6b;
|
||||
}
|
||||
|
||||
|
39
server.js
39
server.js
@@ -1098,7 +1098,7 @@ app.post("/importchat", urlencodedParser, function(request, response){
|
||||
|
||||
});
|
||||
|
||||
app.post('/importworld', urlencodedParser, (request, response) => {
|
||||
app.post('/importworldinfo', urlencodedParser, (request, response) => {
|
||||
if(!request.file) return response.sendStatus(400);
|
||||
|
||||
const filename = request.file.originalname;
|
||||
@@ -1130,12 +1130,37 @@ app.post('/importworld', urlencodedParser, (request, response) => {
|
||||
return response.send({ name: worldName });
|
||||
});
|
||||
|
||||
function findTavernWorldEntry(info, key) {
|
||||
app.post('/editworldinfo', jsonParser, (request, response) => {
|
||||
if (!request.body) {
|
||||
return response.sendStatus(400);
|
||||
}
|
||||
|
||||
if (!request.body.name) {
|
||||
return response.status(400).send('World file must have a name');
|
||||
}
|
||||
|
||||
try {
|
||||
if (!('entries' in request.body.data)) {
|
||||
throw new Error('World info must contain an entries list');
|
||||
}
|
||||
} catch (err) {
|
||||
return response.status(400).send('Is not a valid world info file');
|
||||
}
|
||||
|
||||
const filename = `${request.body.name}.json`;
|
||||
const pathToFile = path.join(directories.worlds, filename);
|
||||
|
||||
fs.writeFileSync(pathToFile, JSON.stringify(request.body.data));
|
||||
|
||||
return response.send({ ok: true });
|
||||
});
|
||||
|
||||
function findTavernWorldEntry(info, key, content) {
|
||||
for (const entryId in info.entries) {
|
||||
const entry = info.entries[entryId];
|
||||
const keyString = entry.key.join(',');
|
||||
|
||||
if (keyString === key) {
|
||||
if (keyString === key && entry.content === content) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
@@ -1300,6 +1325,10 @@ async function validateKoboldWorldInfo(koboldFolderName, koboldWorldInfo, tavern
|
||||
// Other Tavern folders should be deleted (including dupes). If folder name selected is null, then delete anyway to clean-up
|
||||
if (!koboldFolderName || folder.name !== koboldFolderName || existingFolderAlreadyFound) {
|
||||
koboldFoldersToDelete.push(folder.uid);
|
||||
// Should also delete all entries in folder otherwise they will be detached
|
||||
if (Array.isArray(folder.entries)) {
|
||||
koboldEntriesToDelete.push(...folder.entries.map(entry => entry.uid));
|
||||
}
|
||||
}
|
||||
|
||||
// Validate existing entries in Kobold world
|
||||
@@ -1310,7 +1339,7 @@ async function validateKoboldWorldInfo(koboldFolderName, koboldWorldInfo, tavern
|
||||
if (folder.entries?.length) {
|
||||
const foundTavernEntries = [];
|
||||
for (const koboldEntry of folder.entries) {
|
||||
const tavernEntry = findTavernWorldEntry(tavernWorldInfo, koboldEntry.key);
|
||||
const tavernEntry = findTavernWorldEntry(tavernWorldInfo, koboldEntry.key, koboldEntry.content);
|
||||
|
||||
if (tavernEntry) {
|
||||
foundTavernEntries.push(tavernEntry.uid);
|
||||
@@ -1348,7 +1377,7 @@ function isEntryOutOfSync(tavernEntry, koboldEntry) {
|
||||
tavernEntry.selective !== koboldEntry.selective ||
|
||||
tavernEntry.constant !== koboldEntry.constant ||
|
||||
tavernEntry.key.join(',') !== koboldEntry.key ||
|
||||
tavernEntry.keysecondary(',') !== koboldEntry.keysecondary;
|
||||
tavernEntry.keysecondary.join(',') !== koboldEntry.keysecondary;
|
||||
}
|
||||
|
||||
// ** REST CLIENT ASYNC WRAPPERS **
|
||||
|
Reference in New Issue
Block a user