From b99ac92a52837dd9659747e355a9e26963513705 Mon Sep 17 00:00:00 2001 From: Gnome Ann <> Date: Sat, 4 Dec 2021 23:59:28 -0500 Subject: [PATCH] WI folders and WI drag-and-drop --- aiserver.py | 167 +++++++++++- static/application.js | 421 ++++++++++++++++++++++++++---- static/custom.css | 159 ++++++++++- static/jquery-ui.sortable.min.css | 6 + static/jquery-ui.sortable.min.js | 6 + templates/index.html | 6 +- 6 files changed, 688 insertions(+), 77 deletions(-) create mode 100644 static/jquery-ui.sortable.min.css create mode 100644 static/jquery-ui.sortable.min.js diff --git a/aiserver.py b/aiserver.py index 28da1d21..50f0e924 100644 --- a/aiserver.py +++ b/aiserver.py @@ -86,7 +86,9 @@ class vars: authornote = "" # Text submitted to Author's Note field andepth = 3 # How far back in history to append author's note actions = structures.KoboldStoryRegister() # Actions submitted by user and AI - worldinfo = [] # Array of World Info key/value objects + worldinfo = [] # List of World Info key/value objects + wifolders_d = {} # Dictionary of World Info folder UID-info pairs + wifolders_l = [] # List of World Info folder UIDs # badwords = [] # Array of str/chr values that should be removed from output badwordsids = [[13460], [6880], [50256], [42496], [4613], [17414], [22039], [16410], [27], [29], [38430], [37922], [15913], [24618], [28725], [58], [47175], [36937], [26700], [12878], [16471], [37981], [5218], [29795], [13412], [45160], [3693], [49778], [4211], [20598], [36475], [33409], [44167], [32406], [29847], [29342], [42669], [685], [25787], [7359], [3784], [5320], [33994], [33490], [34516], [43734], [17635], [24293], [9959], [23785], [21737], [28401], [18161], [26358], [32509], [1279], [38155], [18189], [26894], [6927], [14610], [23834], [11037], [14631], [26933], [46904], [22330], [25915], [47934], [38214], [1875], [14692], [41832], [13163], [25970], [29565], [44926], [19841], [37250], [49029], [9609], [44438], [16791], [17816], [30109], [41888], [47527], [42924], [23984], [49074], [33717], [31161], [49082], [30138], [31175], [12240], [14804], [7131], [26076], [33250], [3556], [38381], [36338], [32756], [46581], [17912], [49146]] # Tokenized array of badwords used to prevent AI artifacting deletewi = -1 # Temporary storage for index to delete @@ -858,6 +860,8 @@ def download(): "key": wi["key"], "keysecondary": wi["keysecondary"], "content": wi["content"], + "comment": wi["comment"], + "folder": wi["folder"], "selective": wi["selective"], "constant": wi["constant"] }) @@ -1055,17 +1059,49 @@ def get_message(msg): elif(msg['cmd'] == 'wiinit'): if(int(msg['data']) < len(vars.worldinfo)): vars.worldinfo[msg['data']]["init"] = True - addwiitem() + addwiitem(folder_uid=msg['folder']) + elif(msg['cmd'] == 'wifolderinit'): + addwifolder() + elif(msg['cmd'] == 'wimoveitem'): + movewiitem(msg['destination'], msg['data']) + elif(msg['cmd'] == 'wimovefolder'): + movewifolder(msg['destination'], msg['data']) elif(msg['cmd'] == 'widelete'): deletewi(msg['data']) + elif(msg['cmd'] == 'wifolderdelete'): + deletewifolder(msg['data']) + elif(msg['cmd'] == 'wiexpand'): + assert 0 <= int(msg['data']) < len(vars.worldinfo) + emit('from_server', {'cmd': 'wiexpand', 'data': msg['data']}, broadcast=True) + elif(msg['cmd'] == 'wiexpandfolder'): + assert 0 <= int(msg['data']) < len(vars.worldinfo) + emit('from_server', {'cmd': 'wiexpandfolder', 'data': msg['data']}, broadcast=True) + elif(msg['cmd'] == 'wiupdate'): + num = int(msg['num']) + fields = ("key", "keysecondary", "content", "comment") + for field in fields: + if(field in msg['data'] and type(msg['data'][field]) is str): + vars.worldinfo[num][field] = msg['data'][field] + emit('from_server', {'cmd': 'wiupdate', 'num': msg['num'], 'data': {field: vars.worldinfo[num][field] for field in fields}}, broadcast=True) + elif(msg['cmd'] == 'wifolderupdate'): + uid = int(msg['uid']) + fields = ("name", "collapsed") + for field in fields: + if(field in msg['data'] and type(msg['data'][field]) is (str if field != "collapsed" else bool)): + vars.wifolders_d[uid][field] = msg['data'][field] + emit('from_server', {'cmd': 'wifolderupdate', 'uid': msg['uid'], 'data': {field: vars.wifolders_d[uid][field] for field in fields}}, broadcast=True) elif(msg['cmd'] == 'wiselon'): vars.worldinfo[msg['data']]["selective"] = True + emit('from_server', {'cmd': 'wiselon', 'data': msg['data']}, broadcast=True) elif(msg['cmd'] == 'wiseloff'): vars.worldinfo[msg['data']]["selective"] = False + emit('from_server', {'cmd': 'wiseloff', 'data': msg['data']}, broadcast=True) elif(msg['cmd'] == 'wiconstanton'): vars.worldinfo[msg['data']]["constant"] = True + emit('from_server', {'cmd': 'wiconstanton', 'data': msg['data']}, broadcast=True) elif(msg['cmd'] == 'wiconstantoff'): vars.worldinfo[msg['data']]["constant"] = False + emit('from_server', {'cmd': 'wiconstantoff', 'data': msg['data']}, broadcast=True) elif(msg['cmd'] == 'sendwilist'): commitwi(msg['data']) elif(msg['cmd'] == 'aidgimport'): @@ -2104,32 +2140,76 @@ def togglewimode(): #==================================================================# # #==================================================================# -def addwiitem(): - ob = {"key": "", "keysecondary": "", "content": "", "num": len(vars.worldinfo), "init": False, "selective": False, "constant": False} - vars.worldinfo.append(ob); +def addwiitem(folder_uid=None): + assert folder_uid is None or folder_uid in vars.wifolders_d + ob = {"key": "", "keysecondary": "", "content": "", "comment": "", "folder": folder_uid, "num": len(vars.worldinfo), "init": False, "selective": False, "constant": False} + vars.worldinfo.append(ob) emit('from_server', {'cmd': 'addwiitem', 'data': ob}, broadcast=True) +#==================================================================# +# Creates a new WI folder with an unused cryptographically secure random UID +#==================================================================# +def addwifolder(): + while(True): + uid = int.from_bytes(os.urandom(4), "little", signed=True) + if(uid not in vars.wifolders_d): + break + ob = {"name": "", "collapsed": False} + vars.wifolders_d[uid] = ob + vars.wifolders_l.append(uid) + emit('from_server', {'cmd': 'addwifolder', 'uid': uid, 'data': ob}, broadcast=True) + addwiitem(folder_uid=uid) + +#==================================================================# +# Move the WI entry with number src so that it immediately precedes +# the WI entry with number dst +#==================================================================# +def movewiitem(dst, src): + vars.worldinfo[src]["folder"] = vars.worldinfo[dst]["folder"] + vars.worldinfo.insert(dst - (dst >= src), vars.worldinfo.pop(src)) + sendwi() + +#==================================================================# +# Move the WI folder with UID src so that it immediately precedes +# the WI folder with UID dst +#==================================================================# +def movewifolder(dst, src): + vars.wifolders_l.remove(src) + if(dst is None): + # If dst is None, that means we should move src to be the last folder + vars.wifolders_l.append(src) + else: + vars.wifolders_l.insert(vars.wifolders_l.index(dst), src) + sendwi() + #==================================================================# # #==================================================================# def sendwi(): # Cache len of WI ln = len(vars.worldinfo) - + # Clear contents of WI container - emit('from_server', {'cmd': 'clearwi', 'data': ''}, broadcast=True) - + emit('from_server', {'cmd': 'wistart', 'wifolders_d': vars.wifolders_d, 'wifolders_l': vars.wifolders_l, 'data': ''}, broadcast=True) + + # Stable-sort WI entries in order of folder + stablesortwi() + # If there are no WI entries, send an empty WI object if(ln == 0): addwiitem() else: # Send contents of WI array + organizewi() + last_folder = ... for wi in vars.worldinfo: + if(wi["folder"] != last_folder): + emit('from_server', {'cmd': 'addwifolder', 'uid': wi["folder"], 'data': vars.wifolders_d[wi["folder"]] if wi["folder"] is not None else None}, broadcast=True) + last_folder = wi["folder"] ob = wi emit('from_server', {'cmd': 'addwiitem', 'data': ob}, broadcast=True) - # Make sure last WI item is uninitialized - if(vars.worldinfo[-1]["init"]): - addwiitem() + + emit('from_server', {'cmd': 'wifinish', 'data': ''}, broadcast=True) #==================================================================# # Request current contents of all WI HTML elements @@ -2140,6 +2220,25 @@ def requestwi(): list.append(wi["num"]) emit('from_server', {'cmd': 'requestwiitem', 'data': list}) +#==================================================================# +# Stable-sort WI items so that items in the same folder are adjacent, +# and items in different folders are sorted based on the order of the folders +#==================================================================# +def stablesortwi(): + mapping = {uid: index for index, uid in enumerate(vars.wifolders_l)} + vars.worldinfo.sort(key=lambda x: mapping[x["folder"]] if x["folder"] is not None else float("inf")) + last_folder = ... + last_wi = None + for wi in vars.worldinfo: + wi["init"] = True + if(wi["folder"] != last_folder): + if(last_wi is not None and last_folder is not ...): + last_wi["init"] = False + last_folder = wi["folder"] + last_wi = wi + if(last_wi) is not None: + last_wi["init"] = False + #==================================================================# # Renumber WI items consecutively #==================================================================# @@ -2159,12 +2258,13 @@ def commitwi(ar): vars.worldinfo[ob["num"]]["key"] = ob["key"] vars.worldinfo[ob["num"]]["keysecondary"] = ob["keysecondary"] vars.worldinfo[ob["num"]]["content"] = ob["content"] + vars.worldinfo[ob["num"]]["comment"] = ob.get("comment", "") + vars.worldinfo[ob["num"]]["folder"] = ob.get("folder", None) vars.worldinfo[ob["num"]]["selective"] = ob["selective"] vars.worldinfo[ob["num"]]["constant"] = ob.get("constant", False) # Was this a deletion request? If so, remove the requested index if(vars.deletewi >= 0): del vars.worldinfo[vars.deletewi] - organizewi() # Send the new WI array structure sendwi() # And reset deletewi index @@ -2180,6 +2280,23 @@ def deletewi(num): # Get contents of WI HTML inputs requestwi() +#==================================================================# +# +#==================================================================# +def deletewifolder(uid): + uid = int(uid) + del vars.wifolders_d[uid] + del vars.wifolders_l[vars.wifolders_l.index(uid)] + # Delete uninitialized entries in the folder we're going to delete + vars.worldinfo = [wi for wi in vars.worldinfo if wi["folder"] != uid or wi["init"]] + # Move WI entries that are inside of the folder we're going to delete + # so that they're outside of all folders + for wi in vars.worldinfo: + if(wi["folder"] == uid): + wi["folder"] = None + + sendwi() + #==================================================================# # Look for WI keys in text to generator #==================================================================# @@ -2495,6 +2612,8 @@ def saveRequest(savpath): js["authorsnote"] = vars.authornote js["actions"] = tuple(vars.actions.values()) js["worldinfo"] = [] + js["wifolders_d"] = vars.wifolders_d + js["wifolders_l"] = vars.wifolders_l # Extract only the important bits of WI for wi in vars.worldinfo: @@ -2503,6 +2622,8 @@ def saveRequest(savpath): "key": wi["key"], "keysecondary": wi["keysecondary"], "content": wi["content"], + "comment": wi["comment"], + "folder": wi["folder"], "selective": wi["selective"], "constant": wi["constant"] }) @@ -2583,6 +2704,8 @@ def loadRequest(loadpath, filename=None): vars.prompt = js["prompt"] vars.memory = js["memory"] vars.worldinfo = [] + vars.wifolders_d = {int(k): v for k, v in js.get("wifolders_d", {}).items()} + vars.wifolders_l = js.get("wifolders_l", []) vars.lastact = "" vars.lastctx = "" @@ -2615,13 +2738,19 @@ def loadRequest(loadpath, filename=None): "key": wi["key"], "keysecondary": wi.get("keysecondary", ""), "content": wi["content"], + "comment": wi.get("comment", ""), + "folder": wi.get("folder", None), "num": num, "init": True, "selective": wi.get("selective", False), "constant": wi.get("constant", False) }) num += 1 - + + for uid in vars.wifolders_l + [None]: + vars.worldinfo.append({"key": "", "keysecondary": "", "content": "", "comment": "", "folder": uid, "num": None, "init": False, "selective": False, "constant": False}) + stablesortwi() + # Save path for save button vars.savedir = loadpath @@ -2762,6 +2891,8 @@ def importgame(): vars.authornote = ref["authorsNote"] if type(ref["authorsNote"]) is str else "" vars.actions = structures.KoboldStoryRegister() vars.worldinfo = [] + vars.wifolders_d = {} + vars.wifolders_l = [] vars.lastact = "" vars.lastctx = "" @@ -2784,6 +2915,8 @@ def importgame(): "key": wi["keys"], "keysecondary": wi.get("keysecondary", ""), "content": wi["entry"], + "comment": wi.get("comment", ""), + "folder": wi.get("folder", None), "num": num, "init": True, "selective": wi.get("selective", False), @@ -2826,6 +2959,8 @@ def importAidgRequest(id): vars.authornote = js["authorsNote"] vars.actions = structures.KoboldStoryRegister() vars.worldinfo = [] + vars.wifolders_d = {} + vars.wifolders_l = [] vars.lastact = "" vars.lastctx = "" @@ -2835,6 +2970,8 @@ def importAidgRequest(id): "key": wi["keys"], "keysecondary": wi.get("keysecondary", ""), "content": wi["entry"], + "comment": wi.get("comment", ""), + "folder": wi.get("folder", None), "num": num, "init": True, "selective": wi.get("selective", False), @@ -2873,6 +3010,8 @@ def wiimportrequest(): "key": wi["keys"], "keysecondary": wi.get("keysecondary", ""), "content": wi["entry"], + "comment": wi.get("comment", ""), + "folder": wi.get("folder", None), "num": num, "init": True, "selective": wi.get("selective", False), @@ -2900,6 +3039,8 @@ def newGameRequest(): vars.authornote = "" vars.worldinfo = [] + vars.wifolders_d = {} + vars.wifolders_l = [] vars.lastact = "" vars.lastctx = "" diff --git a/static/application.js b/static/application.js index f4d04bea..053c4a32 100644 --- a/static/application.js +++ b/static/application.js @@ -71,6 +71,7 @@ var storyname = null; var memorymode = false; var memorytext = ""; var gamestarted = false; +var wiscroll = 0; var editmode = false; var connected = false; var newly_loaded = true; @@ -78,6 +79,8 @@ var modified_chunks = new Set(); var empty_chunks = new Set(); var gametext_bound = false; var saved_prompt = "..."; +var wifolders_d = {}; +var wifolders_l = []; var override_focusout = false; var sman_allow_delete = false; var sman_allow_rename = false; @@ -196,51 +199,99 @@ function addImportLine(ob) { }); } +function adjustWiCommentHeight(element) { + element.style.height = "0px"; + element.style.height = element.scrollHeight + "px"; + element.parentNode.parentNode.style.height = element.scrollHeight + 90 + "px"; +} + +function adjustWiFolderNameHeight(element) { + element.style.height = "0px"; + element.style.height = element.scrollHeight + "px"; + element.parentNode.parentNode.parentNode.style.height = element.scrollHeight + 19 + "px"; +} + function addWiLine(ob) { + var current_wifolder_element = ob.folder === null ? $(".wisortable-body:not([folder-uid])").last() : $(".wisortable-body[folder-uid="+ob.folder+"]"); if(ob.init) { if(ob.selective){ - wi_menu.append("
\ + current_wifolder_element.append("
\ +
\ + \ +
\ +
\ +
\ + \ +
\ + \ +
\ + \ +
\ + \ +
\ + \ +
\ + \ +
\ + \ +
\ +
\
\ \ \ \
\
\ - \ - \ - \ + \ + \ + \ + \ \
\
\ \
\ -
\ - \ - \ -
\
"); } else { - wi_menu.append("
\ + current_wifolder_element.append("
\ +
\ + \ +
\ +
\ +
\ + \ +
\ + \ +
\ + \ +
\ + \ +
\ + \ +
\ + \ +
\ + \ +
\ +
\
\ \ \ \
\
\ - \ - \ - \ + \ + \ + \ + \ \
\
\ \
\ -
\ - \ - \ -
\
"); } + adjustWiCommentHeight($("#wicomment"+ob.num)[0]); // Send key value to text input $("#wikey"+ob.num).val(ob.key); $("#wikeyprimary"+ob.num).val(ob.key); @@ -251,81 +302,212 @@ function addWiLine(ob) { }); } else { // Show WI line item with form fields hidden (uninitialized) - wi_menu.append("
\ + current_wifolder_element.append("
\ +
\ + \ +
\ +
\ +
\ + \ +
\ + \ +
\ + \ +
\ + \ +
\ + \ +
\ + \ +
\ + \ +
\ +
\
\ \ \ \
\
\ - \ - \ - \ + \ + \ + \ + \ \
\
\ \
\ -
\ - \ - \ -
\
"); // Assign function to expand WI item to button $("#btn_wi"+ob.num).on("click", function () { - expandWiLine(ob.num); + socket.send({'cmd': 'wiexpand', 'data': ob.num}); + socket.send({'cmd': 'wiinit', 'folder': parseInt($("#wilistitem"+ob.num).parent().attr("folder-uid")) || null, 'data': ob.num}); }); } // Assign actions to other elements wientry_onfocus = function () { + $("#selective-key-"+ob.num).addClass("selective-key-icon-clickthrough"); $("#constant-key-"+ob.num).addClass("constant-key-icon-clickthrough"); } wientry_onfocusout = function () { + $("#selective-key-"+ob.num).removeClass("selective-key-icon-clickthrough"); $("#constant-key-"+ob.num).removeClass("constant-key-icon-clickthrough"); + // Tell server about updated WI fields + var selective = $("#wilistitem"+ob.num)[0].classList.contains("wilistitem-selective"); + socket.send({'cmd': 'wiupdate', 'num': ob.num, 'data': { + key: selective ? $("#wikeyprimary"+ob.num).val() : $("#wikey"+ob.num).val(), + keysecondary: $("#wikeysecondary"+ob.num).val(), + content: $("#wientry"+ob.num).val(), + comment: $("#wicomment"+ob.num).val(), + }}); } $("#wikey"+ob.num).on("focus", wientry_onfocus); $("#wikeyprimary"+ob.num).on("focus", wientry_onfocus); $("#wikeysecondary"+ob.num).on("focus", wientry_onfocus); + $("#wientry"+ob.num).on("focus", wientry_onfocus); + $("#wicomment"+ob.num).on("focus", wientry_onfocus); $("#wikey"+ob.num).on("focusout", wientry_onfocusout); $("#wikeyprimary"+ob.num).on("focusout", wientry_onfocusout); $("#wikeysecondary"+ob.num).on("focusout", wientry_onfocusout); + $("#wientry"+ob.num).on("focusout", wientry_onfocusout); + $("#wicomment"+ob.num).on("focusout", wientry_onfocusout); $("#btn_wican"+ob.num).on("click", function () { hideWiDeleteConfirm(ob.num); }); $("#btn_widel"+ob.num).on("click", function () { socket.send({'cmd': 'widelete', 'data': ob.num}); }); - $("#btn_wiselon"+ob.num).on("click", function () { - enableWiSelective(ob.num); - $("#wikey"+ob.num).addClass("wilistitem-selective"); - }); - $("#btn_wiseloff"+ob.num).on("click", function () { - disableWiSelective(ob.num); - $("#wikey"+ob.num).removeClass("wilistitem-selective"); + $("#selective-key-"+ob.num).on("click", function () { + var element = $("#selective-key-"+ob.num); + if(element.hasClass("selective-key-icon-enabled")) { + socket.send({'cmd': 'wiseloff', 'data': ob.num}); + } else { + socket.send({'cmd': 'wiselon', 'data': ob.num}); + } }); $("#constant-key-"+ob.num).on("click", function () { var element = $("#constant-key-"+ob.num); if(element.hasClass("constant-key-icon-enabled")) { socket.send({'cmd': 'wiconstantoff', 'data': ob.num}); - element.removeClass("constant-key-icon-enabled"); - $("#wikey"+ob.num).removeClass("wilistitem-constant"); } else { socket.send({'cmd': 'wiconstanton', 'data': ob.num}); - element.addClass("constant-key-icon-enabled"); - $("#wikey"+ob.num).addClass("wilistitem-constant"); } }); + $("#wihandle"+ob.num).off().on("mousedown", wientry_onfocusout); +} + +function addWiFolder(uid, ob) { + if(uid !== null) { + var uninitialized = $("#wilistfoldercontainer"+null); + var html = "
\ +
\ +
\ + \ + \ + \ +
\ +
\ +
\ + \ +
\ +
\ +
\ +
\ + \ +
\ +
\ +
\ +
\ + \ +
\ + \ +
\ + \ +
\ +
\ +
\ +
\ +
\ +
\ +
"; + if(uninitialized.length) { + $(html).insertBefore(uninitialized); + } else { + wi_menu.append(html); + } + var onfocusout = function () { + socket.send({'cmd': 'wifolderupdate', 'uid': uid, 'data': { + name: $("#wifoldername"+uid).val(), + collapsed: false, + }}); + }; + $("#btn_wifolder"+uid).on("click", function () { + showWiFolderDeleteConfirm(uid); + }); + $("#btn_wifolderdel"+uid).on("click", function () { + socket.send({'cmd': 'wifolderdelete', 'data': uid}); + }); + $("#btn_wifoldercan"+uid).on("click", function () { + hideWiFolderDeleteConfirm(uid); + }) + $("#wifoldername"+uid).on("focusout", onfocusout); + $("#wifolderhandle"+uid).off().on("mousedown", onfocusout); + adjustWiFolderNameHeight($("#wifoldername"+uid)[0]); + } else { + wi_menu.append("
\ +
\ +
\ + \ + \ + \ +
\ +
\ +
\ + \ +
\ +
\ +
\ +
\ + \ +
\ +
\ +
\ +
\ + \ +
\ + \ +
\ + \ +
\ +
\ +
\ +
\ +
\ +
\ +
"); + $("#btn_wifolder"+uid).on("click", function () { + expandWiFolderLine(uid); + }); + } } function expandWiLine(num) { - show([$("#wikey"+num), $("#wientry"+num), $("#constant-key-"+num), $("#btn_wiselon"+num)]); + show([$("#wikey"+num), $("#wientry"+num), $("#wihandle"+num), $("#selective-key-"+num), $("#constant-key-"+num), $("#btn_wiselon"+num), $("#wicomment"+num)]); + $("#wihandle"+num).removeClass("wihandle-inactive").addClass("wihandle"); $("#btn_wi"+num).html("X"); $("#btn_wi"+num).off(); + $("#wilistitem"+num).removeClass("wilistitem-uninitialized").removeClass("wisortable-excluded"); // Tell server the WI entry was initialized - socket.send({'cmd': 'wiinit', 'data': num}); $("#btn_wi"+num).on("click", function () { showWiDeleteConfirm(num); }); + + adjustWiCommentHeight($("#wicomment"+num)[0]); +} + +function expandWiFolderLine(num) { + socket.send({'cmd': 'wifolderinit', 'data': ''}); } function showWiDeleteConfirm(num) { @@ -333,25 +515,51 @@ function showWiDeleteConfirm(num) { show([$("#btn_widel"+num), $("#btn_wican"+num)]); } +function showWiFolderDeleteConfirm(num) { + hide([$("#btn_wifolder"+num)]); + show([$("#btn_wifolderdel"+num), $("#btn_wifoldercan"+num)]); +} + function hideWiDeleteConfirm(num) { show([$("#btn_wi"+num)]); hide([$("#btn_widel"+num), $("#btn_wican"+num)]); } +function hideWiFolderDeleteConfirm(num) { + show([$("#btn_wifolder"+num)]); + hide([$("#btn_wifolderdel"+num), $("#btn_wifoldercan"+num)]); +} + function enableWiSelective(num) { - hide([$("#btn_wiselon"+num), $("#wikey"+num)]); - // Tell server the WI entry is now selective - socket.send({'cmd': 'wiselon', 'data': num}); + hide([$("#wikey"+num)]); $("#wikeyprimary"+num).val($("#wikey"+num).val()); - show([$("#wikeyprimary"+num), $("#wikeysecondary"+num), $("#btn_wiseloff"+num)]); + show([$("#wikeyprimary"+num), $("#wikeysecondary"+num)]); + + var element = $("#selective-key-"+num); + element.addClass("selective-key-icon-enabled"); + $("#wikey"+num).addClass("wilistitem-selective"); } function disableWiSelective(num) { - hide([$("#btn_wiseloff"+num), $("#wikeyprimary"+num), $("#wikeysecondary"+num)]); - // Tell server the WI entry is now non-selective - socket.send({'cmd': 'wiseloff', 'data': num}); + hide([$("#wikeyprimary"+num), $("#wikeysecondary"+num)]); $("#wikey"+num).val($("#wikeyprimary"+num).val()); - show([$("#btn_wiselon"+num), $("#wikey"+num)]); + show([$("#wikey"+num)]); + + var element = $("#selective-key-"+num); + element.removeClass("selective-key-icon-enabled"); + $("#wikey"+num).removeClass("wilistitem-selective"); +} + +function enableWiConstant(num) { + var element = $("#constant-key-"+num); + element.addClass("constant-key-icon-enabled"); + $("#wikey"+num).addClass("wilistitem-constant"); +} + +function disableWiConstant(num) { + var element = $("#constant-key-"+num); + element.removeClass("constant-key-icon-enabled"); + $("#wikey"+num).removeClass("wilistitem-constant"); } function highlightImportLine(ref) { @@ -491,11 +699,13 @@ function returnWiList(ar) { var list = []; var i; for(i=0; i :not(.wisortable-excluded):not(.wisortable-excluded-dynamic), #wimenu .wisortable-container[folder-uid]:not(.wisortable-excluded):not(.wisortable-excluded-dynamic)", + containment: "#wimenu", + connectWith: "#wimenu .wisortable-body", + handle: ".wihandle", + start: sortableOnStart, + stop: sortableOnStop, + cursor: "move", + tolerance: "pointer", + opacity: 0.42, + revert: 173, + scrollSensitivity: 64, + scrollSpeed: 10, + }); + // Restore previously-saved scroll position + $("#gamescreen").scrollTop(wiscroll); + } else if(msg.cmd == "requestwiitem") { // Package WI contents and send back to server returnWiList(msg.data); } else if(msg.cmd == "saveas") { @@ -1846,7 +2151,17 @@ $(document).ready(function(){ anote_slider.on("input", function () { socket.send({'cmd': 'anotedepth', 'data': $(this).val()}); }); - + + // Dynamically change vertical size of world info "Comment" text box + wi_menu.on("input", ".wicomment > textarea", function () { + adjustWiCommentHeight(this); + }); + + // Dynamically change vertical size of world info folder name text box + wi_menu.on("input", ".wifoldername > div > textarea", function () { + adjustWiFolderNameHeight(this); + }); + saveasinput.on("input", function () { if(saveasinput.val() == "") { disableButtons([saveas_accept]); diff --git a/static/custom.css b/static/custom.css index 9ac2ae65..0949a749 100644 --- a/static/custom.css +++ b/static/custom.css @@ -73,6 +73,7 @@ chunk.editing, chunk.editing * { } #gamescreen { + overflow-x: hidden; height: 490px; margin-top: 10px; display: flex; @@ -509,6 +510,70 @@ chunk.editing, chunk.editing * { height: 50%; } +.wiheightfull { + height: 90%; +} + +.wiheighthalf { + height: 45%; +} + +.wicomment { + height: 10%; + grid-column-start: 2; + grid-column-end: 4; + padding-top: 10px; + padding-bottom: 6px; + padding-right: 0px; +} + +.wihandle, .wifolderhandle { + grid-row: span 2; + line-height: 100%; + opacity: 30%; + font-size: 8px; + position: relative; +} + +.wifoldericon { + font-size: 28px; + position: relative; + grid-row: span 2; +} + +.wicentered { + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); + -moz-transform: translate(-50%, -50%); + -webkit-transform: translate(-50%, -50%); + -ms-transform: translate(-50%, -50%); + -o-transform: translate(-50%, -50%); +} + +.wicentered-vertical { + position: absolute; + top: 50%; + transform: translate(0%, -50%); + -moz-transform: translate(0%, -50%); + -webkit-transform: translate(0%, -50%); + -ms-transform: translate(0%, -50%); + -o-transform: translate(0%, -50%); +} + +.wihandle:hover { + cursor: move; +} + +.wisortable-body { + +} + +.wisortable-dummy { + padding: 6px; +} + .helpicon { display: inline-block; font-family: sans-serif; @@ -578,6 +643,45 @@ chunk.editing, chunk.editing * { position: relative; } +.selective-key-icon { + position: absolute !important; + top: 5px !important; + right: 28px !important; + z-index: 1; + opacity: 20%; +} + +*:hover > .selective-key-icon { + opacity: 40%; +} + +.selective-key-icon:hover { + opacity: 65%; + cursor: pointer; +} + +.selective-key-icon-enabled { + color: #3bf723; + opacity: 65% +} + +*:hover > .selective-key-icon-enabled { + opacity: 65%; +} + +.selective-key-icon-enabled:hover { + opacity: 100% +} + +.selective-key-icon-clickthrough { + opacity: 0% !important; + pointer-events: none; +} + +.selective-key-icon-clickthrough.selective-key-icon-enabled { + opacity: 35% !important; +} + .constant-key-icon { position: absolute !important; top: 5px !important; @@ -888,26 +992,62 @@ chunk.editing, chunk.editing * { } .wilistitem { - height: 80px; + height: 120px; display: grid; - grid-template-columns: 4% 30% 58% 8%; - margin-bottom: 10px; + grid-template-columns: 4% 30% 63.5% 2.5%; + margin-bottom: 24px; + background-color: #212122; +} + +.wilistfolder { + height: 60px; + display: grid; + grid-template-columns: 4% 5% 88.5% 2.5%; + margin-bottom: 24px; + background-color: #212122; } .wientry { padding-left: 10px; - padding-right: 10px; + padding-right: 0px; background-color: #212122; } .wientry > textarea { - height: 100%; + height: 90%; resize: none; overflow:auto; background-color: #404040; color: #ffffff; } +.wicomment > textarea { + resize: none; + height: 0px; + overflow: hidden; + background-color: #404040; + color: #ffffff; +} + +.wifoldername { + position: relative; + grid-row: span 2; +} + +.wifoldername > div { + width: 100%; + padding-left: 10px; +} + +.wifoldername > div > textarea { + font-size: 20px; + resize: none; + height: 0px; + overflow: hidden; + background-color: #404040; + color: #ffffff; +} + .wikey { background-color: #212122; } @@ -917,12 +1057,13 @@ chunk.editing, chunk.editing * { color: #ffffff; } +.wiremove { + grid-row-start: 1; + grid-row-end: 3; +} + .wiremove > button { width: 80%; overflow: hidden; font-size: 12pt; } - -.wiselective > button { - white-space: normal; -} diff --git a/static/jquery-ui.sortable.min.css b/static/jquery-ui.sortable.min.css new file mode 100644 index 00000000..16b6fd5a --- /dev/null +++ b/static/jquery-ui.sortable.min.css @@ -0,0 +1,6 @@ +/*! jQuery UI - v1.13.0 - 2021-12-02 +* http://jqueryui.com +* Includes: sortable.css +* Copyright jQuery Foundation and other contributors; Licensed MIT */ + +.ui-sortable-handle{-ms-touch-action:none;touch-action:none} diff --git a/static/jquery-ui.sortable.min.js b/static/jquery-ui.sortable.min.js new file mode 100644 index 00000000..42ea2ecf --- /dev/null +++ b/static/jquery-ui.sortable.min.js @@ -0,0 +1,6 @@ +/*! jQuery UI - v1.13.0 - 2021-12-02 +* http://jqueryui.com +* Includes: widget.js, data.js, scroll-parent.js, widgets/sortable.js, widgets/mouse.js +* Copyright jQuery Foundation and other contributors; Licensed MIT */ + +!function(t){"use strict";"function"==typeof define&&define.amd?define(["jquery"],t):t(jQuery)}(function(f){"use strict";f.ui=f.ui||{};f.ui.version="1.13.0";var o,i=0,r=Array.prototype.hasOwnProperty,h=Array.prototype.slice;f.cleanData=(o=f.cleanData,function(t){for(var e,i,s=0;null!=(i=t[s]);s++)(e=f._data(i,"events"))&&e.remove&&f(i).triggerHandler("remove");o(t)}),f.widget=function(t,i,e){var s,o,n,r={},h=t.split(".")[0],a=h+"-"+(t=t.split(".")[1]);return e||(e=i,i=f.Widget),Array.isArray(e)&&(e=f.extend.apply(null,[{}].concat(e))),f.expr.pseudos[a.toLowerCase()]=function(t){return!!f.data(t,a)},f[h]=f[h]||{},s=f[h][t],o=f[h][t]=function(t,e){if(!this._createWidget)return new o(t,e);arguments.length&&this._createWidget(t,e)},f.extend(o,s,{version:e.version,_proto:f.extend({},e),_childConstructors:[]}),(n=new i).options=f.widget.extend({},n.options),f.each(e,function(e,s){function o(){return i.prototype[e].apply(this,arguments)}function n(t){return i.prototype[e].apply(this,t)}r[e]="function"==typeof s?function(){var t,e=this._super,i=this._superApply;return this._super=o,this._superApply=n,t=s.apply(this,arguments),this._super=e,this._superApply=i,t}:s}),o.prototype=f.widget.extend(n,{widgetEventPrefix:s&&n.widgetEventPrefix||t},r,{constructor:o,namespace:h,widgetName:t,widgetFullName:a}),s?(f.each(s._childConstructors,function(t,e){var i=e.prototype;f.widget(i.namespace+"."+i.widgetName,o,e._proto)}),delete s._childConstructors):i._childConstructors.push(o),f.widget.bridge(t,o),o},f.widget.extend=function(t){for(var e,i,s=h.call(arguments,1),o=0,n=s.length;o",options:{classes:{},disabled:!1,create:null},_createWidget:function(t,e){e=f(e||this.defaultElement||this)[0],this.element=f(e),this.uuid=i++,this.eventNamespace="."+this.widgetName+this.uuid,this.bindings=f(),this.hoverable=f(),this.focusable=f(),this.classesElementLookup={},e!==this&&(f.data(e,this.widgetFullName,this),this._on(!0,this.element,{remove:function(t){t.target===e&&this.destroy()}}),this.document=f(e.style?e.ownerDocument:e.document||e),this.window=f(this.document[0].defaultView||this.document[0].parentWindow)),this.options=f.widget.extend({},this.options,this._getCreateOptions(),t),this._create(),this.options.disabled&&this._setOptionDisabled(this.options.disabled),this._trigger("create",null,this._getCreateEventData()),this._init()},_getCreateOptions:function(){return{}},_getCreateEventData:f.noop,_create:f.noop,_init:f.noop,destroy:function(){var i=this;this._destroy(),f.each(this.classesElementLookup,function(t,e){i._removeClass(e,t)}),this.element.off(this.eventNamespace).removeData(this.widgetFullName),this.widget().off(this.eventNamespace).removeAttr("aria-disabled"),this.bindings.off(this.eventNamespace)},_destroy:f.noop,widget:function(){return this.element},option:function(t,e){var i,s,o,n=t;if(0===arguments.length)return f.widget.extend({},this.options);if("string"==typeof t)if(n={},t=(i=t.split(".")).shift(),i.length){for(s=n[t]=f.widget.extend({},this.options[t]),o=0;o=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return!0}}),f.widget("ui.sortable",f.ui.mouse,{version:"1.13.0",widgetEventPrefix:"sort",ready:!1,options:{appendTo:"parent",axis:!1,connectWith:!1,containment:!1,cursor:"auto",cursorAt:!1,dropOnEmpty:!0,forcePlaceholderSize:!1,forceHelperSize:!1,grid:!1,handle:!1,helper:"original",items:"> *",opacity:!1,placeholder:!1,revert:!1,scroll:!0,scrollSensitivity:20,scrollSpeed:20,scope:"default",tolerance:"intersect",zIndex:1e3,activate:null,beforeStop:null,change:null,deactivate:null,out:null,over:null,receive:null,remove:null,sort:null,start:null,stop:null,update:null},_isOverAxis:function(t,e,i){return e<=t&&t*{ cursor: "+n.cursor+" !important; }").appendTo(o)),n.zIndex&&(this.helper.css("zIndex")&&(this._storedZIndex=this.helper.css("zIndex")),this.helper.css("zIndex",n.zIndex)),n.opacity&&(this.helper.css("opacity")&&(this._storedOpacity=this.helper.css("opacity")),this.helper.css("opacity",n.opacity)),this.scrollParent[0]!==this.document[0]&&"HTML"!==this.scrollParent[0].tagName&&(this.overflowOffset=this.scrollParent.offset()),this._trigger("start",t,this._uiHash()),this._preserveHelperProportions||this._cacheHelperProportions(),!i)for(s=this.containers.length-1;0<=s;s--)this.containers[s]._trigger("activate",t,this._uiHash(this));return f.ui.ddmanager&&(f.ui.ddmanager.current=this),f.ui.ddmanager&&!n.dropBehaviour&&f.ui.ddmanager.prepareOffsets(this,t),this.dragging=!0,this._addClass(this.helper,"ui-sortable-helper"),this.helper.parent().is(this.appendTo)||(this.helper.detach().appendTo(this.appendTo),this.offset.parent=this._getParentOffset()),this.position=this.originalPosition=this._generatePosition(t),this.originalPageX=t.pageX,this.originalPageY=t.pageY,this.lastPositionAbs=this.positionAbs=this._convertPositionTo("absolute"),this._mouseDrag(t),!0},_scroll:function(t){var e=this.options,i=!1;return this.scrollParent[0]!==this.document[0]&&"HTML"!==this.scrollParent[0].tagName?(this.overflowOffset.top+this.scrollParent[0].offsetHeight-t.pageYt[this.floating?"width":"height"]?l&&c:n",i.document[0]);return i._addClass(t,"ui-sortable-placeholder",s||i.currentItem[0].className)._removeClass(t,"ui-sortable-helper"),"tbody"===o?i._createTrPlaceholder(i.currentItem.find("tr").eq(0),f("",i.document[0]).appendTo(t)):"tr"===o?i._createTrPlaceholder(i.currentItem,t):"img"===o&&t.attr("src",i.currentItem.attr("src")),s||t.css("visibility","hidden"),t},update:function(t,e){s&&!n.forcePlaceholderSize||(e.height()&&(!n.forcePlaceholderSize||"tbody"!==o&&"tr"!==o)||e.height(i.currentItem.innerHeight()-parseInt(i.currentItem.css("paddingTop")||0,10)-parseInt(i.currentItem.css("paddingBottom")||0,10)),e.width()||e.width(i.currentItem.innerWidth()-parseInt(i.currentItem.css("paddingLeft")||0,10)-parseInt(i.currentItem.css("paddingRight")||0,10)))}}),i.placeholder=f(n.placeholder.element.call(i.element,i.currentItem)),i.currentItem.after(i.placeholder),n.placeholder.update(i,i.placeholder)},_createTrPlaceholder:function(t,e){var i=this;t.children().each(function(){f(" ",i.document[0]).attr("colspan",f(this).attr("colspan")||1).appendTo(e)})},_contactContainers:function(t){for(var e,i,s,o,n,r,h,a,l,c=null,u=null,p=this.containers.length-1;0<=p;p--)f.contains(this.currentItem[0],this.containers[p].element[0])||(this._intersectsWith(this.containers[p].containerCache)?c&&f.contains(this.containers[p].element[0],c.element[0])||(c=this.containers[p],u=p):this.containers[p].containerCache.over&&(this.containers[p]._trigger("out",t,this._uiHash(this)),this.containers[p].containerCache.over=0));if(this.innermostContainer=c)if(1===this.containers.length)this.containers[u].containerCache.over||(this.containers[u]._trigger("over",t,this._uiHash(this)),this.containers[u].containerCache.over=1);else{for(i=1e4,s=null,o=(a=c.floating||this._isFloating(this.currentItem))?"left":"top",n=a?"width":"height",l=a?"pageX":"pageY",e=this.items.length-1;0<=e;e--)f.contains(this.containers[u].element[0],this.items[e].item[0])&&this.items[e].item[0]!==this.currentItem[0]&&(r=this.items[e].item.offset()[o],h=!1,t[l]-r>this.items[e][n]/2&&(h=!0),Math.abs(t[l]-r)this.containment[2]&&(i=this.containment[2]+this.offset.click.left),t.pageY-this.offset.click.top>this.containment[3]&&(s=this.containment[3]+this.offset.click.top)),e.grid&&(t=this.originalPageY+Math.round((s-this.originalPageY)/e.grid[1])*e.grid[1],s=!this.containment||t-this.offset.click.top>=this.containment[1]&&t-this.offset.click.top<=this.containment[3]?t:t-this.offset.click.top>=this.containment[1]?t-e.grid[1]:t+e.grid[1],t=this.originalPageX+Math.round((i-this.originalPageX)/e.grid[0])*e.grid[0],i=!this.containment||t-this.offset.click.left>=this.containment[0]&&t-this.offset.click.left<=this.containment[2]?t:t-this.offset.click.left>=this.containment[0]?t-e.grid[0]:t+e.grid[0])),{top:s-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+("fixed"===this.cssPosition?-this.scrollParent.scrollTop():n?0:o.scrollTop()),left:i-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():n?0:o.scrollLeft())}},_rearrange:function(t,e,i,s){i?i[0].appendChild(this.placeholder[0]):e.item[0].parentNode.insertBefore(this.placeholder[0],"down"===this.direction?e.item[0]:e.item[0].nextSibling),this.counter=this.counter?++this.counter:1;var o=this.counter;this._delay(function(){o===this.counter&&this.refreshPositions(!s)})},_clear:function(t,e){this.reverting=!1;var i,s=[];if(!this._noFinalSort&&this.currentItem.parent().length&&this.placeholder.before(this.currentItem),this._noFinalSort=null,this.helper[0]===this.currentItem[0]){for(i in this._storedCSS)"auto"!==this._storedCSS[i]&&"static"!==this._storedCSS[i]||(this._storedCSS[i]="");this.currentItem.css(this._storedCSS),this._removeClass(this.currentItem,"ui-sortable-helper")}else this.currentItem.show();function o(e,i,s){return function(t){s._trigger(e,t,i._uiHash(i))}}for(this.fromOutside&&!e&&s.push(function(t){this._trigger("receive",t,this._uiHash(this.fromOutside))}),!this.fromOutside&&this.domPosition.prev===this.currentItem.prev().not(".ui-sortable-helper")[0]&&this.domPosition.parent===this.currentItem.parent()[0]||e||s.push(function(t){this._trigger("update",t,this._uiHash())}),this!==this.currentContainer&&(e||(s.push(function(t){this._trigger("remove",t,this._uiHash())}),s.push(function(e){return function(t){e._trigger("receive",t,this._uiHash(this))}}.call(this,this.currentContainer)),s.push(function(e){return function(t){e._trigger("update",t,this._uiHash(this))}}.call(this,this.currentContainer)))),i=this.containers.length-1;0<=i;i--)e||s.push(o("deactivate",this,this.containers[i])),this.containers[i].containerCache.over&&(s.push(o("out",this,this.containers[i])),this.containers[i].containerCache.over=0);if(this.storedCursor&&(this.document.find("body").css("cursor",this.storedCursor),this.storedStylesheet.remove()),this._storedOpacity&&this.helper.css("opacity",this._storedOpacity),this._storedZIndex&&this.helper.css("zIndex","auto"===this._storedZIndex?"":this._storedZIndex),this.dragging=!1,e||this._trigger("beforeStop",t,this._uiHash()),this.placeholder[0].parentNode.removeChild(this.placeholder[0]),this.cancelHelperRemoval||(this.helper[0]!==this.currentItem[0]&&this.helper.remove(),this.helper=null),!e){for(i=0;i + - + - + +