Initial infinite scroll version

This commit is contained in:
ebolam
2022-10-03 14:21:01 -04:00
parent 5b68c2d64d
commit 31760a19e1
5 changed files with 194 additions and 66 deletions

View File

@@ -495,7 +495,6 @@ from flask_socketio import SocketIO, emit, join_room, leave_room
from flask_socketio import emit as _emit
from flask_session import Session
from flask_compress import Compress
import secrets
from werkzeug.exceptions import HTTPException, NotFound, InternalServerError
import secrets
app = Flask(__name__, root_path=os.getcwd())
@@ -8237,13 +8236,12 @@ def story_sort(base_path, desc=False):
with open(filename, "r") as f:
try:
js = json.load(f)
if 'story_name' in js and js['story_name'] in koboldai_vars.story_loads:
files[file.name] = datetime.datetime.strptime(koboldai_vars.story_loads[js['story_name']], "%m/%d/%Y, %H:%M:%S")
else:
files[file.name] = datetime.datetime.fromtimestamp(file.stat().st_mtime)
except:
pass
if 'story_name' in js and js['story_name'] in koboldai_vars.story_loads:
files[file.name] = datetime.datetime.strptime(koboldai_vars.story_loads[js['story_name']], "%m/%d/%Y, %H:%M:%S")
else:
files[file.name] = datetime.datetime.fromtimestamp(file.stat().st_mtime)
return [key[0] for key in sorted(files.items(), key=lambda kv: (kv[1], kv[0]), reverse=desc)]
@@ -9044,6 +9042,24 @@ def UI_2_refresh_auto_memory(data):
koboldai_vars.auto_memory += "\n\n Final Result:\n" + output
#==================================================================#
# Get next 100 actions for infinate scroll
#==================================================================#
@socketio.on("get_next_100_actions")
@logger.catch
def UI_2_get_next_100_actions(data):
logger.debug("Sending an additional 100 actions, starting at action {}".format(data-1))
sent = 0
data_to_send = []
for i in reversed(list(koboldai_vars.actions.actions)):
if i < data:
if sent >= 50:
break
data_to_send.append({"id": i, "action": koboldai_vars.actions.actions[i]})
sent += 1
emit("var_changed", {"classname": "story", "name": "actions", "old_value": None, "value":data_to_send})
#==================================================================#
# Test
#==================================================================#

View File

@@ -44,19 +44,24 @@ def process_variable_changes(socketio, classname, name, value, old_value, debug_
#logger.debug("sending data to room (multi_story={},classname={}): {}".format(multi_story, classname, room))
#Special Case for KoboldStoryRegister
if isinstance(value, KoboldStoryRegister):
#To speed up loading time we will only transmit the last 100 actions to the UI, then rely on scrolling triggers to load more as needed
if not has_request_context():
if queue is not None:
#logger.debug("Had to use queue")
queue.put(["var_changed", {"classname": "actions", "name": "Action Count", "old_value": None, "value":value.action_count}, {"broadcast":True, "room":room}])
for i in value.actions:
queue.put(["var_changed", {"classname": "story", "name": "actions", "old_value": None, "value":{"id": i, "action": value.actions[i]}}, {"broadcast":True, "room":room}])
data_to_send = []
for i in list(value.actions)[-100:]:
data_to_send.append({"id": i, "action": value.actions[i]})
queue.put(["var_changed", {"classname": "story", "name": "actions", "old_value": None, "value":data_to_send}, {"broadcast":True, "room":room}])
else:
socketio.emit("var_changed", {"classname": "actions", "name": "Action Count", "old_value": None, "value":value.action_count}, broadcast=True, room=room)
for i in value.actions:
socketio.emit("var_changed", {"classname": "story", "name": "actions", "old_value": None, "value":{"id": i, "action": value.actions[i]}}, broadcast=True, room=room)
data_to_send = []
for i in list(value.actions)[-100:]:
data_to_send.append({"id": i, "action": value.actions[i]})
socketio.emit("var_changed", {"classname": "story", "name": "actions", "old_value": None, "value": data_to_send}, broadcast=True, room=room)
elif isinstance(value, KoboldWorldInfo):
value.send_to_ui()
else:
@@ -757,6 +762,22 @@ class story_settings(settings):
new_world_info.socketio = self.socketio
self.worldinfo_v2 = new_world_info
def assign_world_info_to_actions(self, action_id=None, wuid=None):
if action_id is None or action_id not in self.actions.actions:
actions_to_check = self.actions.actions
else:
actions_to_check = {action_id: self.actions.actions[action_id]}
if wuid is None or wuid not in self.worldinfo_v2.world_info:
wi_to_check = self.worldinfo_v2.world_info
else:
wi_to_check = {wuid: self.worldinfo_v2.world_info[wuid]}
for action_id, action in actions_to_check.items():
for uid, wi in wi_to_check.items():
for key in sorted(wi['key'], key=len, reverse=True):
if key in action['Selected Text']:
self.actions.add_wi_to_action(action_id, key, wi['content'])
break
def __setattr__(self, name, value):
new_variable = name not in self.__dict__
@@ -1020,7 +1041,7 @@ class KoboldStoryRegister(object):
self.koboldai_vars = koboldai_vars
#### DO NOT DIRECTLY EDIT THE ACTIONS DICT. IT WILL NOT TRANSMIT TO CLIENT. USE FUCTIONS BELOW TO DO SO ###
#### doing actions[x] = game text is OK
self.actions = {} #keys = "Selected Text", "Options", "Selected Text Length", "In AI Input", "Probabilities".
self.actions = {} #keys = "Selected Text", "WI Search Text", "Wi_highlighted_text", "Options", "Selected Text Length", "In AI Input", "Probabilities".
#Options being a list of dict with keys of "text", "Pinned", "Previous Selection", "Edited", "Probabilities"
self.action_count = -1
self.tokenizer = tokenizer
@@ -1031,6 +1052,14 @@ class KoboldStoryRegister(object):
def reset(self, sequence=[]):
self.__init__(self.socketio, self.story_settings, self.koboldai_vars, sequence=sequence, tokenizer=self.tokenizer)
def add_wi_to_action(action_id, key, content):
#First check to see if we have the wi_highlighted_text variable
if 'wi_highlighted_text' not in self.actions[action_id]:
self.actions[action_id]['wi_highlighted_text'] = [{"text": self.actions[action_id]['Selected Text'], "WI matches": [], "WI Text": ""}]
def __str__(self):
if len(self.actions) > 0:
return "".join([x['Selected Text'] for ignore, x in sorted(self.actions.items())])
@@ -1120,7 +1149,12 @@ class KoboldStoryRegister(object):
temp[int(item)] = json_data['actions'][item]
if "WI Search Text" not in temp[int(item)]:
temp[int(item)]["WI Search Text"] = re.sub("[^0-9a-z \'\"]", "", temp[int(item)]['Selected Text'])
process_variable_changes(self.socketio, "story", 'actions', {"id": item, 'action': temp[int(item)]}, None)
data_to_send = []
if int(item) >= self.action_count-100:
data_to_send.append({"id": item, 'action': temp[int(item)]})
process_variable_changes(self.socketio, "story", 'actions', data_to_send, None)
self.actions = temp
self.set_game_saved()
self.story_settings.save_story()

View File

@@ -2354,7 +2354,7 @@ textarea {
outline: none;
}
/*.pulse {
.pulse {
box-shadow: 0 0 0 0 rgba(255, 255, 255, 1);
animation: pulse-white 2s infinite;
}
@@ -2374,7 +2374,32 @@ textarea {
transform: scale(0.95);
box-shadow: 0 0 0 0 rgba(255, 255, 255, 0);
}
}*/
}
@keyframes pulse-text {
0% {
filter: blur(3px);
-webkit-filter: blur(3px);
}
33% {
filter: blur(2px);
-webkit-filter: blur(2px);
}
66% {
filter: blur(1px);
-webkit-filter: blur(1px);
}
100% {
filter: blur(0px);
-webkit-filter: blur(0px);
}
}
.single_pulse {
animation: pulse-text 2s 1;
}
.drag-over {
border-top: dashed 3px red;

View File

@@ -26,11 +26,11 @@ socket.on("delete_new_world_info_entry", function(data){document.getElementById(
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("error", function(data){show_error_message(data);});
socket.on('load_cookies', function(data){load_cookies(data)});
socket.on('load_cookies', function(data){load_cookies(data);});
socket.on('load_tweaks', function(data){load_tweaks(data);});
socket.on("wi_results", updateWISearchListings);
socket.on("request_prompt_config", configurePrompt);
socket.on("log_message", function(data){process_log_message(data)});
socket.on("log_message", function(data){process_log_message(data);});
socket.on("debug_message", function(data){console.log(data);});
socket.on("scratchpad_response", recieveScratchpadResponse);
//socket.onAny(function(event_name, data) {console.log({"event": event_name, "class": data.classname, "data": data});});
@@ -60,6 +60,7 @@ var finder_waiting_id = null;
var control_held = false;
var actions_data = {};
var setup_wi_toggles = [];
var scroll_trigger_element = undefined;
const on_colab = $el("#on_colab").textContent == "true";
// name, desc, icon, func
@@ -202,11 +203,11 @@ function fix_text(val) {
}
}
function create_options(data) {
function create_options(action) {
//Set all options before the next chunk to hidden
var option_container = document.getElementById("Select Options");
var current_chunk = parseInt(document.getElementById("action_count").textContent)+1;
if (current_chunk != data.value.id.toString()) {
if (current_chunk != action.id.toString()) {
return;
}
if (document.getElementById("Select Options Chunk " + current_chunk)) {
@@ -218,12 +219,12 @@ function create_options(data) {
document.getElementById("Select Options Chunk " + (current_chunk-1)).remove();
}
if (document.getElementById("Select Options Chunk "+data.value.id)) {
var option_chunk = document.getElementById("Select Options Chunk "+data.value.id);
if (document.getElementById("Select Options Chunk "+action.id)) {
var option_chunk = document.getElementById("Select Options Chunk "+action.id);
} else {
var option_chunk = document.createElement("div");
option_chunk.id = "Select Options Chunk "+data.value.id;
if (current_chunk != data.value.id) {
option_chunk.id = "Select Options Chunk "+action.id;
if (current_chunk != action.id) {
option_chunk.classList.add("hidden");
}
option_container.append(option_chunk);
@@ -236,7 +237,7 @@ function create_options(data) {
table.classList.add("sequences");
//Add Redo options
i=0;
for (item of data.value.action.Options) {
for (item of action.action.Options) {
if ((item['Previous Selection'])) {
var row = document.createElement("div");
row.classList.add("sequence_row");
@@ -244,10 +245,10 @@ function create_options(data) {
textcell.textContent = item.text;
textcell.classList.add("sequence");
textcell.setAttribute("option_id", i);
textcell.setAttribute("option_chunk", data.value.id);
textcell.setAttribute("option_chunk", action.id);
var iconcell = document.createElement("span");
iconcell.setAttribute("option_id", i);
iconcell.setAttribute("option_chunk", data.value.id);
iconcell.setAttribute("option_chunk", action.id);
iconcell.classList.add("sequnce_icon");
var icon = document.createElement("span");
icon.id = "Pin_"+i;
@@ -256,7 +257,7 @@ function create_options(data) {
iconcell.append(icon);
delete_icon = $e("span", iconcell, {"classes": ["material-icons-outlined", "cursor", 'delete_option_icon'],
"title": "delete option", 'option_id': i,
'option_chunk': data.value.id, 'textContent': 'delete'});
'option_chunk': action.id, 'textContent': 'delete'});
delete_icon.onclick = function () {
socket.emit("delete_option", {"chunk": this.getAttribute("option_chunk"), "option": this.getAttribute("option_id")});
};
@@ -271,7 +272,7 @@ function create_options(data) {
}
//Add general options
i=0;
for (item of data.value.action.Options) {
for (item of action.action.Options) {
if (!(item.Edited) && !(item['Previous Selection'])) {
var row = document.createElement("div");
row.classList.add("sequence_row");
@@ -279,10 +280,10 @@ function create_options(data) {
textcell.textContent = item.text;
textcell.classList.add("sequence");
textcell.setAttribute("option_id", i);
textcell.setAttribute("option_chunk", data.value.id);
textcell.setAttribute("option_chunk", action.id);
var iconcell = document.createElement("span");
iconcell.setAttribute("option_id", i);
iconcell.setAttribute("option_chunk", data.value.id);
iconcell.setAttribute("option_chunk", action.id);
iconcell.classList.add("sequnce_icon");
var icon = document.createElement("span");
icon.id = "Pin_"+i;
@@ -311,47 +312,61 @@ function create_options(data) {
//option_chunk.scrollIntoView();
}
function do_story_text_updates(data) {
function do_story_text_updates(action) {
story_area = document.getElementById('Selected Text');
current_chunk_number = data.value.id;
if (document.getElementById('Selected Text Chunk '+data.value.id)) {
var item = document.getElementById('Selected Text Chunk '+data.value.id);
current_chunk_number = action.id;
if (document.getElementById('Selected Text Chunk '+action.id)) {
var item = document.getElementById('Selected Text Chunk '+action.id);
//clear out the item first
while (item.firstChild) {
item.removeChild(item.firstChild);
}
span = document.createElement("span");
span.textContent = data.value.action['Selected Text'];
span.textContent = action.action['Selected Text'];
item.append(span);
item.original_text = data.value.action['Selected Text'];
item.setAttribute("WI_Search_Text", data.value.action['WI Search Text']);
item.original_text = action.action['Selected Text'];
item.setAttribute("WI_Search_Text", action.action['WI Search Text']);
item.setAttribute("world_info_uids", "");
item.classList.remove("pulse")
item.classList.remove("single_pulse");
item.classList.add("single_pulse");
//item.scrollIntoView();
if (item.textContent != "") {
assign_world_info_to_action(data.value.id, null);
assign_world_info_to_action(action.id, null);
}
} else {
var span = document.createElement("span");
span.id = 'Selected Text Chunk '+data.value.id;
span.id = 'Selected Text Chunk '+action.id;
span.classList.add("rawtext");
span.setAttribute("chunk", data.value.id);
span.original_text = data.value.action['Selected Text'];
span.setAttribute("chunk", action.id);
span.original_text = action.action['Selected Text'];
new_span = document.createElement("span");
new_span.textContent = data.value.action['Selected Text'];
span.setAttribute("WI_Search_Text", data.value.action['WI Search Text']);
new_span.textContent = action.action['Selected Text'];
span.setAttribute("WI_Search_Text", action.action['WI Search Text']);
span.append(new_span);
//need to find the closest element
next_id = action.id+1;
while (true) {
if (next_id in actions_data) {
story_area.insertBefore(span, document.getElementById('Selected Text Chunk '+next_id));
break;
} else if (Math.max.apply(null,Object.keys(actions_data)) <= next_id) {
story_area.append(span);
break;
}
next_id += 1;
}
span.classList.add("single_pulse");
story_area.append(span);
if (data.value.id.toString() == document.getElementById('action_count').textContent) {
if (action.id.toString() == document.getElementById('action_count').textContent) {
console.log("Scrolling. Action Count: "+document.getElementById('action_count').textContent);
document.getElementById("Selected Text").scrollTop = document.getElementById("Selected Text").scrollHeight;
}
//clearTimeout(game_text_scroll_timeout);
//game_text_scroll_timeout = setTimeout(function() {document.getElementById("Selected Text").scrollTop = document.getElementById("Selected Text").scrollHeight;}, 500);
if (span.textContent != "") {
assign_world_info_to_action(data.value.id, null);
assign_world_info_to_action(action.id, null);
}
//console.log("done");
}
@@ -393,23 +408,24 @@ function do_prompt(data) {
}
function do_story_text_length_updates(data) {
if (document.getElementById('Selected Text Chunk '+data.value.id)) {
document.getElementById('Selected Text Chunk '+data.value.id).setAttribute("token_length", data.value.action["Selected Text Length"]);
function do_story_text_length_updates(action) {
if (document.getElementById('Selected Text Chunk '+action.id)) {
document.getElementById('Selected Text Chunk '+action.id).setAttribute("token_length", action.action["Selected Text Length"]);
} else {
console.log('Selected Text Chunk '+data.value.id);
console.log('Selected Text Chunk '+action.id);
console.log(action);
}
}
function do_probabilities(data) {
function do_probabilities(action) {
//console.log(data);
if (document.getElementById('probabilities_'+data.value.id)) {
prob_area = document.getElementById('probabilities_'+data.value.id)
if (document.getElementById('probabilities_'+action.id)) {
prob_area = document.getElementById('probabilities_'+action.id)
} else {
probabilities = document.getElementById('probabilities');
prob_area = document.createElement('span');
prob_area.id = 'probabilities_'+data.value.id;
prob_area.id = 'probabilities_'+action.id;
probabilities.append(prob_area);
}
//Clear
@@ -419,8 +435,8 @@ function do_probabilities(data) {
//create table
table = document.createElement("table");
table.border=1;
if ("Probabilities" in data.value.action) {
for (token of data.value.action.Probabilities) {
if ("Probabilities" in action.action) {
for (token of action.action.Probabilities) {
actual_text = document.createElement("td");
actual_text.setAttribute("rowspan", token.length);
actual_text.textContent = "Word Goes Here";
@@ -537,15 +553,40 @@ function var_changed(data) {
}
//Special Case for Actions
if ((data.classname == "story") && (data.name == "actions")) {
actions_data[data.value.id] = data.value.action;
do_story_text_updates(data);
create_options(data);
do_story_text_length_updates(data);
do_probabilities(data);
if (data.value.action['In AI Input']) {
document.getElementById('Selected Text Chunk '+data.value.id).classList.add("within_max_length");
if (Array.isArray(data.value)) {
actions = data.value;
} else {
document.getElementById('Selected Text Chunk '+data.value.id).classList.remove("within_max_length");
actions = [data.value];
}
for (action of actions) {
if (action.length != 0) {
actions_data[action.id] = action.action;
do_story_text_updates(action);
create_options(action);
do_story_text_length_updates(action);
if ('Probabilities' in action.action) {
do_probabilities(action);
}
if (action.action['In AI Input']) {
document.getElementById('Selected Text Chunk '+action.id).classList.add("within_max_length");
} else {
document.getElementById('Selected Text Chunk '+action.id).classList.remove("within_max_length");
}
}
}
//check to see if our new action is before the scroll triggering action
if (actions.length > 0) {
if ((scroll_trigger_element == undefined) || (actions[actions.length-1].id < parseInt(scroll_trigger_element.getAttribute("chunk")))) {
if (scroll_trigger_element != undefined) {
scroll_trigger_element.scrollIntoView();
}
scroll_trigger_element = document.getElementById("Selected Text Chunk "+actions[actions.length-1].id);
//if we hit the top, unhide the prompt and clear the scroll trigger
if (actions[actions.length-1].id == 0) {
document.getElementById("story_prompt").classList.remove("story_prompt");
scroll_trigger_element = null;
}
}
}
//Special Case for Presets
@@ -3191,7 +3232,8 @@ function assign_world_info_to_action(action_item, uid) {
var worldinfo_to_check = world_info_data;
}
if (action_item != null) {
var actions = [actions_data[action_item]];
var actions = {};
actions[action_item] = actions_data[action_item]
} else {
var actions = actions_data;
}
@@ -3234,7 +3276,7 @@ function assign_world_info_to_action(action_item, uid) {
function highlight_world_info_text_in_chunk(action_id, wi) {
//First let's assign our world info id to the action so we know to count the tokens for the world info
let uid = wi['uid'];
action = document.getElementById("Selected Text Chunk "+action_id);
let action = document.getElementById("Selected Text Chunk "+action_id);
let words = action.textContent.split(" ");
current_ids = action.getAttribute("world_info_uids")?action.getAttribute("world_info_uids").split(','):[];
if (!(current_ids.includes(uid))) {
@@ -4741,3 +4783,14 @@ document.addEventListener("keydown", function(event) {
break;
}
});
//function to load more actions if nessisary
document.getElementById("Selected Text").onscroll = function(){
//TOP
if ((scroll_trigger_element != undefined) && (scroll_trigger_element != null)) {
if(scroll_trigger_element.getBoundingClientRect().top >= 0){
console.log("Asking for more actions");
socket.emit("get_next_100_actions", parseInt(scroll_trigger_element.getAttribute("chunk")));
}
}
}

View File

@@ -46,7 +46,7 @@
<div class="gamescreen" id="gamescreen">
<div id="disconnect_message"><center><h1>Disconnected</h1></center></div>
<div class="gametext" id="Selected Text" contenteditable=false onblur="select_game_text(null);" onclick="select_game_text(null);" onkeyup="select_game_text(event);">
<span id="story_prompt" class="var_sync_story_prompt var_sync_alt_story_prompt_length var_sync_alt_story_prompt_in_ai rawtext"></span>
<span id="story_prompt" class="var_sync_story_prompt var_sync_alt_story_prompt_length var_sync_alt_story_prompt_in_ai rawtext hidden"></span>
<div id="Delete Me" class="noselect" contenteditable=false>
<span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;