Merge pull request #416 from one-some/wi-fixes

(mostly) wi fixes and polish
This commit is contained in:
henk717
2023-07-31 20:52:48 +02:00
committed by GitHub
8 changed files with 278 additions and 206 deletions

View File

@@ -4396,8 +4396,8 @@ def requestwi():
# and items in different folders are sorted based on the order of the folders # and items in different folders are sorted based on the order of the folders
#==================================================================# #==================================================================#
def stablesortwi(): def stablesortwi():
mapping = {uid: index for index, uid in enumerate(koboldai_vars.wifolders_l)} mapping = {int(uid): index for index, uid in enumerate(koboldai_vars.wifolders_l)}
koboldai_vars.worldinfo.sort(key=lambda x: mapping[str(x["folder"])] if x["folder"] is not None else float("inf")) koboldai_vars.worldinfo.sort(key=lambda x: mapping[int(x["folder"])] if x["folder"] is not None else float("inf"))
last_folder = ... last_folder = ...
last_wi = None last_wi = None
for i, wi in enumerate(koboldai_vars.worldinfo): for i, wi in enumerate(koboldai_vars.worldinfo):
@@ -6692,11 +6692,18 @@ def UI_2_set_wi_image(uid):
except FileNotFoundError: except FileNotFoundError:
pass pass
else: else:
# Otherwise assign image try:
with open(path, "wb") as file: # Otherwise assign image
file.write(data) with open(path, "wb") as file:
file.write(data)
except FileNotFoundError:
show_error_notification(
"Unable to write image",
"Please save the game before uploading images."
)
return ":(", 500
koboldai_vars.gamesaved = False koboldai_vars.gamesaved = False
return ":)" return ":)", 200
@app.route("/get_wi_image/<int(signed=True):uid>", methods=["GET"]) @app.route("/get_wi_image/<int(signed=True):uid>", methods=["GET"])
@require_allowed_ip @require_allowed_ip

View File

@@ -296,7 +296,7 @@ gensettingstf = [
"max": 1, "max": 1,
"step": 1, "step": 1,
"default": 0, "default": 0,
"tooltip": "Scans the AI's output for World Info keys as it is generating the one.", "tooltip": "Look for World Info keys in the AI's response while it is still being generated.",
"menu_path": "World Info", "menu_path": "World Info",
"sub_path": "", "sub_path": "",
"classname": "story", "classname": "story",
@@ -756,7 +756,7 @@ gensettingstf = [
"max": 1, "max": 1,
"step": 1, "step": 1,
"default": 0, "default": 0,
"tooltip": "If enabled, experimental features will be displayed in the UI.", "tooltip": "If enabled, experimental features will be displayed in the UI. Note: These features have been determined to be too unstable for standard use, and may corrupt your data. You're on your own from here.",
"menu_path": "Interface", "menu_path": "Interface",
"sub_path": "UI", "sub_path": "UI",
"classname": "system", "classname": "system",

View File

@@ -637,7 +637,12 @@ class InferenceModel:
) )
time_end = round(time.time() - time_start, 2) time_end = round(time.time() - time_start, 2)
tokens_per_second = round(len(result.encoded[0]) / time_end, 2)
try:
tokens_per_second = round(len(result.encoded[0]) / time_end, 2)
except ZeroDivisionError:
# Introducing KoboldAI's fastest model: ReadOnly!
tokens_per_second = 0
if not utils.koboldai_vars.quiet: if not utils.koboldai_vars.quiet:
logger.info( logger.info(

View File

@@ -1,12 +1,10 @@
from __future__ import annotations from __future__ import annotations
import torch import torch
import requests
import numpy as np import numpy as np
from typing import List, Optional, Union from typing import List, Optional, Union
import utils import utils
from logger import logger
from modeling.inference_model import ( from modeling.inference_model import (
GenerationResult, GenerationResult,
GenerationSettings, GenerationSettings,
@@ -15,29 +13,46 @@ from modeling.inference_model import (
) )
model_backend_name = "Read Only" model_backend_name = "Read Only"
model_backend_type = "Read Only" #This should be a generic name in case multiple model backends are compatible (think Hugging Face Custom and Basic Hugging Face) model_backend_type = "Read Only" # This should be a generic name in case multiple model backends are compatible (think Hugging Face Custom and Basic Hugging Face)
class BasicAPIException(Exception):
"""To be used for errors when using the Basic API as an interface.""" class DummyHFTokenizerOut:
input_ids = np.array([[]])
class FacadeTokenizer:
def __init__(self):
self._koboldai_header = []
def decode(self, _input):
return ""
def encode(self, input_text):
return []
def __call__(self, *args, **kwargs) -> DummyHFTokenizerOut:
return DummyHFTokenizerOut()
class model_backend(InferenceModel): class model_backend(InferenceModel):
def __init__(self) -> None: def __init__(self) -> None:
super().__init__() super().__init__()
# Do not allow API to be served over the API # Do not allow ReadOnly to be served over the API
self.capabilties = ModelCapabilities(api_host=False) self.capabilties = ModelCapabilities(api_host=False)
self.tokenizer = self._tokenizer() self.tokenizer: FacadeTokenizer = None
self.model = None self.model = None
self.model_name = "Read Only" self.model_name = "Read Only"
def is_valid(self, model_name, model_path, menu_path): def is_valid(self, model_name, model_path, menu_path):
return model_name == "ReadOnly" return model_name == "ReadOnly"
def get_requested_parameters(self, model_name, model_path, menu_path, parameters = {}): def get_requested_parameters(
self, model_name, model_path, menu_path, parameters={}
):
requested_parameters = [] requested_parameters = []
return requested_parameters return requested_parameters
def set_input_parameters(self, parameters): def set_input_parameters(self, parameters):
return return
@@ -46,17 +61,9 @@ class model_backend(InferenceModel):
def _initialize_model(self): def _initialize_model(self):
return return
class _tokenizer():
def __init__(self):
self._koboldai_header = []
def decode(self, _input):
return ""
def encode(self, input_text):
return []
def _load(self, save_model: bool = False, initial_load: bool = False) -> None: def _load(self, save_model: bool = False, initial_load: bool = False) -> None:
self.tokenizer = self.tokenizer self.tokenizer = FacadeTokenizer()
self.model = None self.model = None
utils.koboldai_vars.noai = True utils.koboldai_vars.noai = True
@@ -72,7 +79,7 @@ class model_backend(InferenceModel):
): ):
return GenerationResult( return GenerationResult(
model=self, model=self,
out_batches=np.array([]), out_batches=np.array([[]]),
prompt=prompt_tokens, prompt=prompt_tokens,
is_whole_generation=True, is_whole_generation=True,
single_line=single_line, single_line=single_line,

View File

@@ -1259,17 +1259,46 @@ td.server_vars {
.world_info_label_container > .generate-button:hover { opacity: 1.0; } .world_info_label_container > .generate-button:hover { opacity: 1.0; }
.tag { .tag {
display: inline-block;
background-color: var(--wi_tag_color); background-color: var(--wi_tag_color);
color: var(--wi_tag_text_color); color: var(--wi_tag_text_color);
margin-right: 3px;
margin-top: 3px;
padding: 2px; padding: 2px;
margin-right: 2px; padding-left: 3px;
padding-right: 3px;
border-radius: var(--radius_wi_card); border-radius: var(--radius_wi_card);
border: solid; }
border-color: var(--wi_tag_color);
.tag .tag_button {
cursor: pointer;
opacity: 0.4;
font-size: 16px;
position: relative;
} }
.tag .delete_icon { .tag .delete_icon {
cursor: pointer; top: 3px;
right: 3px;
}
.tag .add_icon {
top: 3px;
right: 3px;
}
.tag .tag_text {
display: inline-block;
outline: none;
position: relative;
right: 3px;
}
.placeholder_tag .tag_text:empty {
opacity: 0.4;
} }
.oi[folder] { .oi[folder] {
@@ -2851,6 +2880,10 @@ body {
height: 100%; height: 100%;
} }
#welcome_text a {
text-decoration: underline;
}
.welcome_text { .welcome_text {
display: flex; display: flex;
height: 100%; height: 100%;
@@ -3569,10 +3602,15 @@ h2 .material-icons-outlined {
} }
.section_header { .section_header {
font-weight: bold;
margin-left: 2px; margin-left: 2px;
margin-bottom: 2px; margin-bottom: 2px;
} }
.story_category_area > * > label {
user-select: none
}
.help_text { .help_text {
margin-left: 6px; margin-left: 6px;
margin-bottom: 0.7em; margin-bottom: 0.7em;

View File

@@ -23,7 +23,13 @@ socket.on('error_popup', function(data){error_popup(data);});
socket.on("world_info_entry", function(data){process_world_info_entry(data);}); socket.on("world_info_entry", function(data){process_world_info_entry(data);});
socket.on("world_info_entry_used_in_game", function(data){world_info_entry_used_in_game(data);}); socket.on("world_info_entry_used_in_game", function(data){world_info_entry_used_in_game(data);});
socket.on("world_info_folder", function(data){world_info_folder(data);}); socket.on("world_info_folder", function(data){world_info_folder(data);});
socket.on("delete_new_world_info_entry", function(data){document.getElementById("world_info_-1").remove();}); socket.on("delete_new_world_info_entry", function(data) {
const card = $el("#world_info_-1");
// Prevent weird race condition/strange event call order where blur event
// fires before removal is finished on Chrome
card.removing = true
card.remove();
});
socket.on("delete_world_info_entry", function(data){document.getElementById("world_info_"+data).remove();}); socket.on("delete_world_info_entry", function(data){document.getElementById("world_info_"+data).remove();});
socket.on("delete_world_info_folder", function(data){document.getElementById("world_info_folder_"+data).remove();}); socket.on("delete_world_info_folder", function(data){document.getElementById("world_info_folder_"+data).remove();});
socket.on("error", function(data){show_error_message(data);}); socket.on("error", function(data){show_error_message(data);});
@@ -87,6 +93,7 @@ var initial_socketio_connection_occured = false;
var selected_model_data; var selected_model_data;
var supported_gen_modes = []; var supported_gen_modes = [];
var privacy_mode_enabled = false; var privacy_mode_enabled = false;
var attention_wanting_wi_bar = null;
var ai_busy = false; var ai_busy = false;
var can_show_options = false; var can_show_options = false;
@@ -1281,7 +1288,7 @@ function redrawPopup() {
delete_icon.setAttribute("tooltip", "Delete"); delete_icon.setAttribute("tooltip", "Delete");
delete_icon.id = row.path; delete_icon.id = row.path;
delete_icon.setAttribute("folder", row.isFolder); delete_icon.setAttribute("folder", row.isFolder);
delete_icon.onclick = function () { delete_icon.addEventListener("click", function(event) {
const message = this.getAttribute("folder") == "true" ? "Do you really want to delete this folder and ALL files under it?" : "Do you really want to delete this file?"; const message = this.getAttribute("folder") == "true" ? "Do you really want to delete this folder and ALL files under it?" : "Do you really want to delete this file?";
const delId = this.id; const delId = this.id;
@@ -1291,9 +1298,11 @@ function redrawPopup() {
denyText="I've changed my mind!", denyText="I've changed my mind!",
confirmCallback=function() { confirmCallback=function() {
socket.emit("popup_delete", delId); socket.emit("popup_delete", delId);
} },
null,
event.shiftKey
); );
}; });
} }
icon_area.append(delete_icon); icon_area.append(delete_icon);
tr.append(icon_area); tr.append(icon_area);
@@ -2203,6 +2212,7 @@ function world_info_entry(data) {
} else { } else {
world_info_card.classList.remove("used_in_game"); world_info_card.classList.remove("used_in_game");
} }
const title = world_info_card.querySelector('.world_info_title'); const title = world_info_card.querySelector('.world_info_title');
title.id = "world_info_title_"+data.uid; title.id = "world_info_title_"+data.uid;
title.textContent = data.title; title.textContent = data.title;
@@ -2210,7 +2220,7 @@ function world_info_entry(data) {
title.setAttribute("original_text", data.title); title.setAttribute("original_text", data.title);
title.setAttribute("contenteditable", true); title.setAttribute("contenteditable", true);
title.classList.remove("pulse"); title.classList.remove("pulse");
title.ondragstart=function() {event.preventDefault();event.stopPropagation();}; title.ondragstart=function(event) {event.preventDefault();event.stopPropagation();};
title.onblur = function () { title.onblur = function () {
this.parentElement.parentElement.setAttribute('draggable', 'true'); this.parentElement.parentElement.setAttribute('draggable', 'true');
this.setAttribute('draggable', 'true'); this.setAttribute('draggable', 'true');
@@ -2220,20 +2230,31 @@ function world_info_entry(data) {
this.classList.add("pulse"); this.classList.add("pulse");
} }
} }
world_info_card.addEventListener('dragstart', dragStart);
world_info_card.addEventListener('dragend', dragend); title.addEventListener("keydown", function(event) {
if (event.key === "Enter") {
event.preventDefault();
this.blur();
}
});
title.addEventListener('dragenter', dragEnter) title.addEventListener('dragenter', dragEnter)
title.addEventListener('dragover', dragOver); title.addEventListener('dragover', dragOver);
title.addEventListener('dragleave', dragLeave); title.addEventListener('dragleave', dragLeave);
title.addEventListener('drop', drop); title.addEventListener('drop', drop);
delete_icon = world_info_card.querySelector('.world_info_delete');
world_info_card.addEventListener('dragstart', dragStart);
world_info_card.addEventListener('dragend', dragend);
const delete_icon = world_info_card.querySelector('.world_info_delete');
delete_icon.id = "world_info_delete_"+data.uid; delete_icon.id = "world_info_delete_"+data.uid;
delete_icon.setAttribute("uid", data.uid); delete_icon.setAttribute("uid", data.uid);
delete_icon.setAttribute("wi-title", data.title); delete_icon.setAttribute("wi-title", data.title);
delete_icon.onclick = function () { delete_icon.addEventListener("click", function (event) {
const wiTitle = this.getAttribute("wi-title"); const wiTitle = this.getAttribute("wi-title");
const wiUid = parseInt(this.getAttribute("uid")); const wiUid = parseInt(this.getAttribute("uid"));
const wiElement = this.parentElement.parentElement; const wiElement = this.parentElement.parentElement;
deleteConfirmation([ deleteConfirmation([
{text: "You're about to delete World Info entry "}, {text: "You're about to delete World Info entry "},
{text: wiTitle, format: "bold"}, {text: wiTitle, format: "bold"},
@@ -2247,9 +2268,11 @@ function world_info_entry(data) {
} else { } else {
socket.emit("delete_world_info", wiUid); socket.emit("delete_world_info", wiUid);
} }
} },
null,
event.shiftKey
); );
} });
const wiImgContainer = world_info_card.querySelector(".world_info_image_container"); const wiImgContainer = world_info_card.querySelector(".world_info_image_container");
const wiImg = wiImgContainer.querySelector(".world_info_image"); const wiImg = wiImgContainer.querySelector(".world_info_image");
@@ -2342,15 +2365,16 @@ function world_info_entry(data) {
this.classList.add("pulse"); this.classList.add("pulse");
}) })
tags = world_info_card.querySelector('.world_info_tag_primary_area'); const tags = world_info_card.querySelector('.world_info_tag_primary_area');
tags.id = "world_info_tags_"+data.uid; tags.id = "world_info_tags_"+data.uid;
//add tag content here //add tag content here
add_tags(tags, data); add_tags(tags, data, "primary");
secondarytags = world_info_card.querySelector('.world_info_tag_secondary_area'); const secondarytags = world_info_card.querySelector('.world_info_tag_secondary_area');
secondarytags.id = "world_info_secondtags_"+data.uid; secondarytags.id = "world_info_secondtags_"+data.uid;
//add second tag content here //add second tag content here
add_secondary_tags(secondarytags, data); add_tags(secondarytags, data, "secondary");
//w++ toggle //w++ toggle
wpp_toggle_area = world_info_card.querySelector('.world_info_wpp_toggle_area'); wpp_toggle_area = world_info_card.querySelector('.world_info_wpp_toggle_area');
wpp_toggle_area.id = "world_info_wpp_toggle_area_"+data.uid; wpp_toggle_area.id = "world_info_wpp_toggle_area_"+data.uid;
@@ -2723,8 +2747,9 @@ function world_info_folder(data) {
delete_button.classList.add("cursor"); delete_button.classList.add("cursor");
delete_button.setAttribute("folder", folder_name); delete_button.setAttribute("folder", folder_name);
delete_button.textContent = "delete"; delete_button.textContent = "delete";
delete_button.onclick = function () { delete_button.addEventListener("click", function (event) {
const folderName = this.getAttribute("folder"); const folderName = this.getAttribute("folder");
deleteConfirmation([ deleteConfirmation([
{text: "You're about to delete World Info folder "}, {text: "You're about to delete World Info folder "},
{text: folderName, format: "bold"}, {text: folderName, format: "bold"},
@@ -2734,9 +2759,11 @@ function world_info_folder(data) {
], ],
confirmText="Go for it.", confirmText="Go for it.",
denyText="I've changed my mind!", denyText="I've changed my mind!",
confirmCallback=function() { socket.emit("delete_wi_folder", folderName); } confirmCallback=function() { socket.emit("delete_wi_folder", folderName); },
null,
event.shiftKey
); );
}; });
delete_button.classList.add("delete"); delete_button.classList.add("delete");
title.append(delete_button); title.append(delete_button);
@@ -3241,6 +3268,8 @@ function upload_file_without_save(file_box) {
} }
function send_world_info(uid) { function send_world_info(uid) {
const cardEl = document.getElementById(`world_info_${uid}`);
if (cardEl.removing) return;
socket.emit("edit_world_info", world_info_data[uid]); socket.emit("edit_world_info", world_info_data[uid]);
} }
@@ -4443,161 +4472,139 @@ function removeA(arr) {
return arr; return arr;
} }
function add_tags(tags, data) { function create_tag_element(tagText, uid, tagType) {
while (tags.firstChild) { // tagText is string, or null for empty tag at end.
tags.removeChild(tags.firstChild); // barType should be "primary" or "secondary"
const isPlaceholderTag = tagText === null;
const wiCardEl = document.querySelector(`.world_info_card[uid="${uid}"]`)
const keyField = {primary: "key", secondary: "keysecondary"}[tagType];
const tagClassFragment = {primary: "tags", primary: "secondtags"}[tagType];
const tagEl = document.createElement("span");
tagEl.classList.add("tag");
if (isPlaceholderTag) tagEl.classList.add("placeholder_tag");
const xEl = document.createElement("span");
xEl.classList.add("material-icons-outlined");
xEl.classList.add("tag_button");
if (!isPlaceholderTag) {
xEl.classList.add("delete_icon");
xEl.textContent = "close";
} else {
xEl.classList.add("add_icon");
xEl.textContent = "add";
} }
for (tag of data.key) {
tag_item = document.createElement("span"); xEl.setAttribute("uid", uid);
tag_item.classList.add("tag"); xEl.setAttribute("tag", tagText);
x = document.createElement("span"); xEl.addEventListener("click", function() {
x.textContent = "x "; removeA(
x.classList.add("delete_icon"); world_info_data[uid][keyField],
x.setAttribute("uid", data.uid); tagText
x.setAttribute("tag", tag); );
x.onclick = function () { send_world_info(uid);
removeA(world_info_data[this.getAttribute('uid')]['key'], this.getAttribute('tag')); this.classList.add("pulse");
send_world_info(this.getAttribute('uid')); });
this.classList.add("pulse");
}; const textEl = document.createElement("span");
text = document.createElement("span"); textEl.classList.add("tag_text");
text.textContent = tag; textEl.textContent = tagText;
text.setAttribute("contenteditable", true);
text.setAttribute("uid", data.uid); textEl.setAttribute("data-placeholder", "Tag")
text.setAttribute("tag", tag); textEl.setAttribute("contenteditable", true);
text.id = "world_info_tags_text_"+data.uid+"_"+tag; textEl.setAttribute("uid", uid);
text.ondragstart=function() {event.preventDefault();event.stopPropagation();}; textEl.setAttribute("tag", tagText);
text.setAttribute("draggable", "true"); textEl.setAttribute("draggable", "true");
text.onfocus=function() {this.parentElement.parentElement.parentElement.setAttribute('draggable', 'false');this.setAttribute('draggable', 'false');}; textEl.id = `world_info_${tagClassFragment}_text_${uid}_${tagText || "blank"}`;
text.onblur = function () {
this.parentElement.parentElement.parentElement.setAttribute('draggable', 'true'); textEl.addEventListener("dragstart", function(event) {
this.setAttribute('draggable', 'true'); event.preventDefault();
for (var i = 0; i < world_info_data[this.getAttribute('uid')]['key'].length; i++) { event.stopPropagation();
if (world_info_data[this.getAttribute('uid')]['key'][i] == this.getAttribute("tag")) { });
world_info_data[this.getAttribute('uid')]['key'][i] = this.textContent;
} textEl.addEventListener("focus", function(event) {
} wiCardEl.setAttribute('draggable', 'false');
send_world_info(this.getAttribute('uid')); this.setAttribute('draggable', 'false');
this.classList.add("pulse"); });
};
tag_item.append(x); textEl.addEventListener("blur", function () {
tag_item.append(text); wiCardEl.setAttribute('draggable', 'true');
tag_item.id = "world_info_tags_"+data.uid+"_"+tag; this.setAttribute('draggable', 'true');
tags.append(tag_item);
} if (!isPlaceholderTag) {
//add the blank tag // Normal tag
tag_item = document.createElement("span"); for (var i = 0; i < world_info_data[uid][keyField].length; i++) {
tag_item.classList.add("tag"); if (world_info_data[uid][keyField][i] !== tagText) {
x = document.createElement("span"); world_info_data[uid][keyField][i] = this.innerText;
x.textContent = "+ "; }
tag_item.append(x); }
text = document.createElement("span"); } else {
text.classList.add("rawtext"); // Placeholder tag
text.textContent = " "; if (!this.textContent.trim()) return;
text.setAttribute("uid", data.uid);
text.setAttribute("contenteditable", true); on_new_wi_item = this.id;
text.id = "world_info_tags_text_"+data.uid+"_blank"; world_info_data[uid][keyField].push(this.textContent);
text.ondragstart=function() {event.preventDefault();event.stopPropagation();}; }
text.setAttribute("draggable", "true");
text.onfocus=function() {this.parentElement.parentElement.parentElement.setAttribute('draggable', 'false');this.setAttribute('draggable', 'false');}; send_world_info(uid);
text.onblur = function () { this.classList.add("pulse");
this.parentElement.parentElement.parentElement.setAttribute('draggable', 'true'); });
this.setAttribute('draggable', 'true');
if (this.textContent.trim() != "") { textEl.addEventListener("keydown", function(event) {
//console.log(this.textContent); if (event.key === "Enter") {
on_new_wi_item = this.id; // Press Enter to save tag and focus next one
world_info_data[this.getAttribute('uid')]['key'].push(this.textContent); event.preventDefault();
send_world_info(this.getAttribute('uid'));
this.classList.add("pulse"); // HACK: Work around the fact that the server is in control of
} else { // placing these elements
this.textContent = " "; attention_wanting_wi_bar = tagType;
} // And don't wait for like 10000 years to randomly take focus from
}; // the user
text.onclick = function () { setTimeout(() => attention_wanting_wi_bar = null, 500);
this.textContent = "";
}; this.blur();
tag_item.append(text); } else if (event.key === "Escape") {
tag_item.id = "world_info_secondtags_"+data.uid+"_new";
tags.append(tag_item); }
})
tagEl.append(xEl);
tagEl.append(textEl);
tagEl.id = `world_info_${tagClassFragment}_${uid}_${tagText || "new"}`;
return tagEl;
} }
function add_secondary_tags(tags, data) { function add_tags(tagBarEl, data, tagType) {
while (tags.firstChild) { // tagType is either "primary" or "secondary"
tags.removeChild(tags.firstChild);
// Remove existing tags
while (tagBarEl.firstChild) {
tagBarEl.removeChild(tagBarEl.firstChild);
} }
for (tag of data.keysecondary) {
tag_item = document.createElement("span"); const tagList = {
tag_item.classList.add("tag"); primary: data.key,
x = document.createElement("span"); secondary: data.keysecondary
x.textContent = "x "; }[tagType];
x.classList.add("delete_icon");
x.setAttribute("uid", data.uid); for (tag of tagList) {
x.setAttribute("tag", tag); tagBarEl.append(create_tag_element(tag, data.uid, tagType));
x.onclick = function () {
removeA(world_info_data[this.getAttribute('uid')]['keysecondary'], this.getAttribute('tag'));
send_world_info(this.getAttribute('uid'));
this.classList.add("pulse");
};
text = document.createElement("span");
text.textContent = tag;
text.setAttribute("contenteditable", true);
text.setAttribute("uid", data.uid);
text.setAttribute("tag", tag);
text.id = "world_info_secondtags_text_"+data.uid+"_"+tag;
text.ondragstart=function() {event.preventDefault();event.stopPropagation();};
text.setAttribute("draggable", "true");
text.onfocus=function() {this.parentElement.parentElement.parentElement.setAttribute('draggable', 'false');this.setAttribute('draggable', 'false');};
text.onblur = function () {
this.parentElement.parentElement.parentElement.setAttribute('draggable', 'true');
this.setAttribute('draggable', 'true');
for (var i = 0; i < world_info_data[this.getAttribute('uid')]['keysecondary'].length; i++) {
if (world_info_data[this.getAttribute('uid')]['keysecondary'][i] == this.getAttribute("tag")) {
world_info_data[this.getAttribute('uid')]['keysecondary'][i] = this.textContent;
}
}
send_world_info(this.getAttribute('uid'));
this.classList.add("pulse");
};
tag_item.append(x);
tag_item.append(text);
tag_item.id = "world_info_secondtags_"+data.uid+"_"+tag;
tags.append(tag_item);
} }
//add the blank tag //add the blank tag
tag_item = document.createElement("span"); const placeholderTagEl = create_tag_element(null, data.uid, tagType);
tag_item.classList.add("tag"); tagBarEl.append(placeholderTagEl);
x = document.createElement("span");
x.textContent = "+ "; if (attention_wanting_wi_bar === tagType) {
tag_item.append(x); const textEl = placeholderTagEl.querySelector(".tag_text");
text = document.createElement("span"); // HACK: Please don't ask because I do not know
text.classList.add("rawtext"); setTimeout(() => textEl.focus(), 1);
text.textContent = " "; }
text.setAttribute("uid", data.uid);
text.setAttribute("contenteditable", true);
text.id = "world_info_secondtags_text_"+data.uid+"_blank";
text.ondragstart=function() {event.preventDefault();event.stopPropagation();};
text.setAttribute("draggable", "true");
text.onfocus=function() {this.parentElement.parentElement.parentElement.setAttribute('draggable', 'false');this.setAttribute('draggable', 'false');};
text.onblur = function () {
this.parentElement.parentElement.parentElement.setAttribute('draggable', 'true');
this.setAttribute('draggable', 'true');
if (this.textContent.trim() != "") {
on_new_wi_item = this.id;
world_info_data[this.getAttribute('uid')]['keysecondary'].push(this.textContent);
send_world_info(this.getAttribute('uid'));
this.classList.add("pulse");
} else {
this.textContent = " ";
}
};
text.onclick = function () {
this.textContent = "";
};
tag_item.append(text);
tag_item.id = "world_info_secondtags_"+data.uid+"_new";
tags.append(tag_item);
} }
function create_new_wi_entry(folder) { function create_new_wi_entry(folder) {
var uid = -1; var uid = -1;
for (item of document.getElementsByClassName('world_info_card')) { for (item of document.getElementsByClassName('world_info_card')) {
@@ -6947,9 +6954,13 @@ function sFormatted2HTML(sFormatted) {
return outHTML; return outHTML;
} }
function deleteConfirmation(sFormatted, confirmText, denyText, confirmCallback, denyCallback) { function deleteConfirmation(sFormatted, confirmText, denyText, confirmCallback, denyCallback=null, bypass=false) {
if (bypass) {
confirmCallback();
return;
}
$el("#confirm-text").innerHTML = sFormatted2HTML(sFormatted); $el("#confirm-text").innerHTML = sFormatted2HTML(sFormatted);
$el("#confirm-confirm-button > .text").innerText = confirmText; $el("#confirm-confirm-button > .text").innerText = confirmText;
$el("#confirm-deny-button > .text").innerText = denyText; $el("#confirm-deny-button > .text").innerText = denyText;

View File

@@ -97,7 +97,7 @@
<div id="story_menu_wi" class="story_category_area tab-target tab-target-story hidden"> <div id="story_menu_wi" class="story_category_area tab-target tab-target-story hidden">
<h4 class="section_header" style="margin-left: 12px;">World Info</h4> <h4 class="section_header" style="margin-left: 12px;">World Info</h4>
<div class="help_text" style="margin-left: 20px;"> <div class="help_text" style="margin-left: 20px;">
Lore information, which the AI recalls by certain words. Lore information, which the AI recalls with the mention of certain words.
<span class="helpicon material-icons-outlined" tooltip="Use this instead of Memory for information on things like characters, objects, events, places, and anything else with detail.">help_icon</span> <span class="helpicon material-icons-outlined" tooltip="Use this instead of Memory for information on things like characters, objects, events, places, and anything else with detail.">help_icon</span>
</div> </div>
<div class="setting_tile_area wi_settings"> <div class="setting_tile_area wi_settings">

View File

@@ -22,12 +22,16 @@
<span <span
class="world_info_item_type" class="world_info_item_type"
contenteditable="true" contenteditable="true"
data-placeholder="Person" data-placeholder="..."
spellcheck="false" spellcheck="false"
></span> <span class="helpicon material-icons-outlined" tooltip="Please enter a noun that describes a person, place or thing." "]">help_icon</span> ></span>
<span
class="helpicon material-icons-outlined"
tooltip='Please enter a noun that describes this entry. For example, "person", "weapon", or "building". This will be used with the Generate Content button below.'
>help_icon</span>
</div> </div>
</div> </div>
<span id="world_info_delete_" class="world_info_delete">X</span> <span id="world_info_delete_" class="world_info_delete material-icons-outlined">close</span>
</div> </div>
<div class="world_info_upper_container world_info_tag_area"> <div class="world_info_upper_container world_info_tag_area">