diff --git a/aiserver.py b/aiserver.py index d22ba7c1..1a5db449 100644 --- a/aiserver.py +++ b/aiserver.py @@ -112,9 +112,11 @@ class vars: lua_kobold = None # `kobold` from` bridge.lua lua_koboldcore = None # `koboldcore` from bridge.lua lua_logname = ... # Name of previous userscript that logged to terminal + lua_running = False # Whether or not Lua is running (i.e. wasn't stopped due to an error) lua_edited = set() # Set of chunk numbers that were edited from a Lua generation modifier lua_deleted = set() # Set of chunk numbers that were deleted from a Lua generation modifier userscripts = [] # List of userscripts to load + last_userscripts = [] # List of previous userscript filenames from the previous time userscripts were send via usstatitems corescript = "default.lua" # Filename of corescript to load # 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 @@ -139,6 +141,7 @@ class vars: importjs = {} # Temporary storage for import data loadselect = "" # Temporary storage for story filename to load spselect = "" # Temporary storage for soft prompt filename to load + spmeta = None # Metadata of current soft prompt, or None if not using a soft prompt sp = None # Current soft prompt tensor (as a NumPy array) sp_length = 0 # Length of current soft prompt in tokens, or 0 if not using a soft prompt svowname = "" # Filename that was flagged for overwrite confirm @@ -1039,10 +1042,13 @@ def load_lua_scripts(): vars.lua_koboldbridge.obliterate_multiverse() tpool.execute(vars.lua_koboldbridge.load_corescript, vars.corescript) tpool.execute(vars.lua_koboldbridge.load_userscripts, filenames, modulenames, descriptions) + vars.lua_running = True except lupa.LuaError as e: vars.lua_koboldbridge.obliterate_multiverse() + vars.lua_running = False if(vars.serverstarted): emit('from_server', {'cmd': 'errmsg', 'data': 'Lua script error, please check console.'}, broadcast=True) + sendUSStatItems() print("{0}{1}{2}".format(colors.RED, "***LUA ERROR***: ", colors.END), end="", file=sys.stderr) print("{0}{1}{2}".format(colors.RED, str(e).replace("\033", ""), colors.END), file=sys.stderr) print("{0}{1}{2}".format(colors.YELLOW, "Lua engine stopped; please open 'Userscripts' and press Load to reinitialize scripts.", colors.END), file=sys.stderr) @@ -1414,7 +1420,9 @@ def execute_inmod(): tpool.execute(vars.lua_koboldbridge.execute_inmod) except lupa.LuaError as e: vars.lua_koboldbridge.obliterate_multiverse() + vars.lua_running = False emit('from_server', {'cmd': 'errmsg', 'data': 'Lua script error, please check console.'}, broadcast=True) + sendUSStatItems() print("{0}{1}{2}".format(colors.RED, "***LUA ERROR***: ", colors.END), end="", file=sys.stderr) print("{0}{1}{2}".format(colors.RED, str(e).replace("\033", ""), colors.END), file=sys.stderr) print("{0}{1}{2}".format(colors.YELLOW, "Lua engine stopped; please open 'Userscripts' and press Load to reinitialize scripts.", colors.END), file=sys.stderr) @@ -1430,7 +1438,9 @@ def execute_outmod(): tpool.execute(vars.lua_koboldbridge.execute_outmod) except lupa.LuaError as e: vars.lua_koboldbridge.obliterate_multiverse() + vars.lua_running = False emit('from_server', {'cmd': 'errmsg', 'data': 'Lua script error, please check console.'}, broadcast=True) + sendUSStatItems() print("{0}{1}{2}".format(colors.RED, "***LUA ERROR***: ", colors.END), end="", file=sys.stderr) print("{0}{1}{2}".format(colors.RED, str(e).replace("\033", ""), colors.END), file=sys.stderr) print("{0}{1}{2}".format(colors.YELLOW, "Lua engine stopped; please open 'Userscripts' and press Load to reinitialize scripts.", colors.END), file=sys.stderr) @@ -1515,7 +1525,10 @@ def do_connect(): emit('from_server', {'cmd': 'runs_remotely'}) if(vars.allowsp): emit('from_server', {'cmd': 'allowsp', 'data': vars.allowsp}) - + + sendUSStatItems() + emit('from_server', {'cmd': 'spstatitems', 'data': {vars.spselect: vars.spmeta} if len(vars.spselect) else {}}, broadcast=True) + if(not vars.gamestarted): setStartState() sendsettings() @@ -1751,7 +1764,8 @@ def get_message(msg): elif(msg['cmd'] == 'splistrequest'): getsplist() elif(msg['cmd'] == 'uslistrequest'): - getuslist() + unloaded, loaded = getuslist() + emit('from_server', {'cmd': 'buildus', 'data': {"unloaded": unloaded, "loaded": loaded}}) elif(msg['cmd'] == 'usloaded'): vars.userscripts = [] for userscript in msg['data']: @@ -1763,8 +1777,8 @@ def get_message(msg): settingschanged() elif(msg['cmd'] == 'usload'): load_lua_scripts() - elif(msg['cmd'] == 'usload'): - getuslist() + unloaded, loaded = getuslist() + sendUSStatItems() elif(msg['cmd'] == 'loadselect'): vars.loadselect = msg["data"] elif(msg['cmd'] == 'spselect'): @@ -1773,6 +1787,7 @@ def get_message(msg): loadRequest(fileops.storypath(vars.loadselect)) elif(msg['cmd'] == 'sprequest'): spRequest(vars.spselect) + emit('from_server', {'cmd': 'spstatitems', 'data': {vars.spselect: vars.spmeta} if len(vars.spselect) else {}}, broadcast=True) elif(msg['cmd'] == 'deletestory'): deletesave(msg['data']) elif(msg['cmd'] == 'renamestory'): @@ -1810,7 +1825,17 @@ def get_message(msg): refresh_settings() elif(not vars.remote and msg['cmd'] == 'importwi'): wiimportrequest() - + +#==================================================================# +# Send userscripts list to client +#==================================================================# +def sendUSStatItems(): + _, loaded = getuslist() + loaded = loaded if vars.lua_running else [] + last_userscripts = [e["filename"] for e in loaded] + emit('from_server', {'cmd': 'usstatitems', 'data': loaded, 'flash': last_userscripts != vars.last_userscripts}, broadcast=True) + vars.last_userscripts = last_userscripts + #==================================================================# # Send start message and tell Javascript to set UI state #==================================================================# @@ -2443,7 +2468,9 @@ def generate(txt, minimum, maximum, found_entries=None): except Exception as e: if(issubclass(type(e), lupa.LuaError)): vars.lua_koboldbridge.obliterate_multiverse() + vars.lua_running = False emit('from_server', {'cmd': 'errmsg', 'data': 'Lua script error, please check console.'}, broadcast=True) + sendUSStatItems() print("{0}{1}{2}".format(colors.RED, "***LUA ERROR***: ", colors.END), end="", file=sys.stderr) print("{0}{1}{2}".format(colors.RED, str(e).replace("\033", ""), colors.END), file=sys.stderr) print("{0}{1}{2}".format(colors.YELLOW, "Lua engine stopped; please open 'Userscripts' and press Load to reinitialize scripts.", colors.END), file=sys.stderr) @@ -2670,7 +2697,9 @@ def tpumtjgenerate(txt, minimum, maximum, found_entries=None): except Exception as e: if(issubclass(type(e), lupa.LuaError)): vars.lua_koboldbridge.obliterate_multiverse() + vars.lua_running = False emit('from_server', {'cmd': 'errmsg', 'data': 'Lua script error, please check console.'}, broadcast=True) + sendUSStatItems() print("{0}{1}{2}".format(colors.RED, "***LUA ERROR***: ", colors.END), end="", file=sys.stderr) print("{0}{1}{2}".format(colors.RED, str(e).replace("\033", ""), colors.END), file=sys.stderr) print("{0}{1}{2}".format(colors.YELLOW, "Lua engine stopped; please open 'Userscripts' and press Load to reinitialize scripts.", colors.END), file=sys.stderr) @@ -3548,7 +3577,7 @@ def getsplist(): emit('from_server', {'cmd': 'buildsp', 'data': fileops.getspfiles(vars.modeldim)}) #==================================================================# -# Show list of userscripts +# Get list of userscripts #==================================================================# def getuslist(): files = {i: v for i, v in enumerate(fileops.getusfiles())} @@ -3556,11 +3585,14 @@ def getuslist(): unloaded = [] userscripts = set(vars.userscripts) for i in range(len(files)): - if files[i]["filename"] in userscripts: - loaded.append(files[i]) - else: + if files[i]["filename"] not in userscripts: unloaded.append(files[i]) - emit('from_server', {'cmd': 'buildus', 'data': {"unloaded": unloaded, "loaded": loaded}}) + files = {files[k]["filename"]: files[k] for k in files} + userscripts = set(files.keys()) + for filename in vars.userscripts: + if filename in userscripts: + loaded.append(files[filename]) + return unloaded, loaded #==================================================================# # Load a saved story via file browser @@ -3693,6 +3725,8 @@ def spRequest(filename): z, version, shape, fortran_order, dtype = fileops.checksp(filename, vars.modeldim) assert isinstance(z, zipfile.ZipFile) + with z.open('meta.json') as f: + vars.spmeta = json.load(f) z.close() with np.load(fileops.sppath(filename), allow_pickle=False) as f: diff --git a/static/application.js b/static/application.js index 5746f05e..410f2809 100644 --- a/static/application.js +++ b/static/application.js @@ -1053,6 +1053,59 @@ function hideRandomStoryPopup() { rspopup.addClass("hidden"); } +function statFlash(ref) { + ref.addClass("status-flash"); + setTimeout(function () { + ref.addClass("colorfade"); + ref.removeClass("status-flash"); + setTimeout(function () { + ref.removeClass("colorfade"); + }, 1000); + }, 50); +} + +function updateUSStatItems(items, flash) { + var stat_us = $("#stat-us"); + var stat_usactive = $("#stat-usactive"); + if(flash || stat_usactive.find("li").length != items.length) { + statFlash(stat_us.closest(".statusicon").add("#usiconlabel")); + } + stat_usactive.html(""); + if(items.length == 0) { + stat_us.html("No userscripts active"); + $("#usiconlabel").html(""); + stat_us.closest(".statusicon").removeClass("active"); + return; + } + stat_us.html("Active userscripts:"); + stat_us.closest(".statusicon").addClass("active"); + var i; + for(i = 0; i < items.length; i++) { + stat_usactive.append($("
  • "+items[i].modulename+" <"+items[i].filename+">
  • ")); + } + $("#usiconlabel").html(items.length); +} + +function updateSPStatItems(items) { + var stat_sp = $("#stat-sp"); + var stat_spactive = $("#stat-spactive"); + var key = null; + var old_val = stat_spactive.html(); + Object.keys(items).forEach(function(k) {key = k;}); + if(key === null) { + stat_sp.html("No soft prompt active"); + stat_sp.closest(".statusicon").removeClass("active"); + stat_spactive.html(""); + } else { + stat_sp.html("Active soft prompt:"); + stat_sp.closest(".statusicon").addClass("active"); + stat_spactive.html((items[key].name || key)+" <"+key+">"); + } + if(stat_spactive.html() !== old_val) { + statFlash(stat_sp.closest(".statusicon")); + } +} + function setStartState() { enableSendBtn(); enableButtons([button_actmem, button_actwi]); @@ -1905,6 +1958,10 @@ $(document).ready(function(){ } else if(msg.cmd == "allowtoggle") { // Allow toggle change states to propagate allowtoggle = msg.data; + } else if(msg.cmd == "usstatitems") { + updateUSStatItems(msg.data, msg.flash); + } else if(msg.cmd == "spstatitems") { + updateSPStatItems(msg.data); } else if(msg.cmd == "popupshow") { // Show/Hide Popup popupShow(msg.data); @@ -2060,6 +2117,8 @@ $(document).ready(function(){ connect_status.html("Lost connection..."); connect_status.removeClass("color_green"); connect_status.addClass("color_orange"); + updateUSStatItems([], false); + updateSPStatItems({}); }); // Register editing events diff --git a/static/custom.css b/static/custom.css index 5bc6139b..d17c0c1c 100644 --- a/static/custom.css +++ b/static/custom.css @@ -29,11 +29,12 @@ chunk.editing, chunk.editing * { #topmenu { background-color: #337ab7; padding: 10px; + display: flex; } #menuitems { - display: grid; - grid-template-columns: 80% 20%; + display: flex; + width: 100%; } #navbar { @@ -66,10 +67,7 @@ chunk.editing, chunk.editing * { #connectstatusdiv { display: flex; -} - -#connectstatusdiv span { - align-self: center; + text-align: right; } #gamescreen { @@ -437,10 +435,10 @@ chunk.editing, chunk.editing * { } .colorfade, .colorfade * { - -moz-transition:color 1s ease-in; - -o-transition:color 1s ease-in; - -webkit-transition:color 1s ease-in; - transition:color 1s ease-in; + -moz-transition: color 1s ease-in, text-shadow 1s ease-in; + -o-transition: color 1s ease-in, text-shadow 1s ease-in; + -webkit-transition: color 1s ease-in, text-shadow 1s ease-in; + transition: color 1s ease-in, text-shadow 1s ease-in; } .color_orange { @@ -484,6 +482,11 @@ chunk.editing, chunk.editing * { color: #3bf723 !important; } +.status-flash, .status-flash { + color: #fce94f !important; + text-shadow: 0 0 50px #fce94f, 0 0 50px #fce94f, 0 0 10px #fce94f, 0 0 10px #fce94f, 0 0 10px #fce94f, 0 0 10px #fce94f, 0 0 10px #fce94f; +} + .flex { display: flex; align-items: center; @@ -656,11 +659,30 @@ chunk.editing, chunk.editing * { text-decoration: none; } -.helpicon:hover { - cursor: pointer; +.statusicon { + display: inline-block; + font-weight: bold; + text-align: center; + padding-left: 8px; + padding-right: 8px; + font-size: 30px !important; + font-weight: bold; + text-align: center; + font-size: 1.4ex; + line-height: 1.8ex; + text-decoration: none; + color: #68a2d4; } -.helpicon:hover .helptext { +.statusicon.active { + color: #3bf723; +} + +.helpicon:hover, .statusicon:hover { + cursor: pointer; +} + +.helpicon:hover .helptext, .statusicon:hover .statustext, .statusicon.statustoggled .statustext { display: inline-block; width: 250px; background-color: #1f2931; @@ -672,13 +694,59 @@ chunk.editing, chunk.editing * { padding: 15px; margin-left:10px; border: 1px solid #337ab7; - - position: absolute; - z-index: 1; } -.helptext { +.statusicon:hover .statustext.statustext-wide, .statusicon.statustoggled .statustext.statustext-wide { + width: 350px; +} + +.statusiconlabel { + pointer-events: none; + color: #337ab7; + text-align: center; + font-weight: bold; + font-size: 13px; +} + +#usiconlabel { + transform: translate(-3px, 10px); + -moz-transform: translate(-3px, 10px); + -webkit-transform: translate(-3px, 10px); + -ms-transform: translate(-3px, 10px); + -o-transform: translate(-3px, 10px); +} + +.status-container { + z-index: 1; + text-shadow: none !important; +} + +.helptext, .statustext { display: none; + font-family: sans-serif; + position: absolute; + z-index: 1; + text-shadow: none !important; +} + +.statustext { + transform: translate(-105%, 30px); + -moz-transform: translate(-105%, 30px); + -webkit-transform: translate(-105%, 30px); + -ms-transform: translate(-105%, 30px); + -o-transform: translate(-105%, 30px); +} + +.statusheader { + padding-bottom: 10px; +} + +#stat-usactive { + text-align: left; + height: 270px; + overflow-y: scroll; + position: relative; + padding-left: 20px; } .justifyleft { @@ -915,7 +983,7 @@ chunk.editing, chunk.editing * { } .navcontainer { - + width: 100%; } .nowrap { diff --git a/templates/index.html b/templates/index.html index 45fcb78e..12939306 100644 --- a/templates/index.html +++ b/templates/index.html @@ -10,12 +10,12 @@ - + - + @@ -80,8 +80,23 @@ -
    - Waiting for connection... +
    + Waiting for connection... +
    + +
    +
    +