Merge pull request #49 from VE-FORBRYDERNE/gui-and-scripting
Scripting and GUI improvements
This commit is contained in:
commit
726b42889b
142
aiserver.py
142
aiserver.py
|
@ -82,6 +82,7 @@ class vars:
|
|||
submission = "" # Same as above, but after applying input formatting
|
||||
lastctx = "" # The last context submitted to the generator
|
||||
model = "" # Model ID string chosen at startup
|
||||
model_orig = "" # Original model string before being changed by auto model type detection
|
||||
model_type = "" # Model Type (Automatically taken from the model config)
|
||||
noai = False # Runs the script without starting up the transformers pipeline
|
||||
aibusy = False # Stops submissions while the AI is working
|
||||
|
@ -113,9 +114,12 @@ 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
|
||||
spfilename = "" # Filename of soft prompt to load, or an empty string if not using a soft prompt
|
||||
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
|
||||
|
@ -140,6 +144,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
|
||||
|
@ -180,7 +185,7 @@ def getModelSelection():
|
|||
while(vars.model == ''):
|
||||
modelsel = input("Model #> ")
|
||||
if(modelsel.isnumeric() and int(modelsel) > 0 and int(modelsel) <= len(modellist)):
|
||||
vars.model = modellist[int(modelsel)-1][1]
|
||||
vars.model = vars.model_orig = modellist[int(modelsel)-1][1]
|
||||
else:
|
||||
print("{0}Please enter a valid selection.{1}".format(colors.RED, colors.END))
|
||||
|
||||
|
@ -361,7 +366,7 @@ parser.add_argument("--override_rename", action='store_true', help="Renaming sto
|
|||
parser.add_argument("--configname", help="Force a fixed configuration name to aid with config management.")
|
||||
|
||||
args = parser.parse_args()
|
||||
vars.model = args.model;
|
||||
vars.model = vars.model_orig = args.model;
|
||||
|
||||
if args.remote:
|
||||
vars.remote = True;
|
||||
|
@ -646,7 +651,22 @@ if(not vars.model in ["InferKit", "Colab", "OAI", "ReadOnly", "TPUMeshTransforme
|
|||
|
||||
|
||||
# Patch transformers to use our custom logit warpers
|
||||
from transformers import LogitsProcessorList, LogitsWarper, LogitsProcessor, TopKLogitsWarper, TopPLogitsWarper, TemperatureLogitsWarper
|
||||
from transformers import LogitsProcessorList, LogitsWarper, LogitsProcessor, TopKLogitsWarper, TopPLogitsWarper, TemperatureLogitsWarper, RepetitionPenaltyLogitsProcessor
|
||||
|
||||
def dynamic_processor_wrap(cls, field_name, var_name, cond=None):
|
||||
old_call = cls.__call__
|
||||
def new_call(self, *args, **kwargs):
|
||||
setattr(self, field_name, getattr(vars, var_name))
|
||||
assert len(args) == 2
|
||||
if(cond is None or cond(getattr(vars, var_name))):
|
||||
return old_call(self, *args, **kwargs)
|
||||
return args[1]
|
||||
cls.__call__ = new_call
|
||||
dynamic_processor_wrap(RepetitionPenaltyLogitsProcessor, "penalty", "rep_pen", cond=lambda x: x != 1.0)
|
||||
dynamic_processor_wrap(TopKLogitsWarper, "top_k", "top_k", cond=lambda x: x > 0)
|
||||
dynamic_processor_wrap(TopPLogitsWarper, "top_p", "top_p", cond=lambda x: x < 1.0)
|
||||
dynamic_processor_wrap(TemperatureLogitsWarper, "temperature", "temp", cond=lambda x: x != 1.0)
|
||||
|
||||
class TailFreeLogitsWarper(LogitsWarper):
|
||||
|
||||
def __init__(self, tfs: float, filter_value: float = -float("Inf"), min_tokens_to_keep: int = 1):
|
||||
|
@ -658,6 +678,8 @@ if(not vars.model in ["InferKit", "Colab", "OAI", "ReadOnly", "TPUMeshTransforme
|
|||
self.min_tokens_to_keep = min_tokens_to_keep
|
||||
|
||||
def __call__(self, input_ids: torch.LongTensor, scores: torch.FloatTensor) -> torch.FloatTensor:
|
||||
self.tfs = vars.tfs
|
||||
|
||||
if self.filter_value >= 1.0:
|
||||
return scores
|
||||
sorted_logits, sorted_indices = torch.sort(scores, descending=True)
|
||||
|
@ -725,31 +747,17 @@ if(not vars.model in ["InferKit", "Colab", "OAI", "ReadOnly", "TPUMeshTransforme
|
|||
new_get_logits_processor.old_get_logits_processor = transformers.generation_utils.GenerationMixin._get_logits_processor
|
||||
transformers.generation_utils.GenerationMixin._get_logits_processor = new_get_logits_processor
|
||||
|
||||
def new_get_logits_warper(
|
||||
top_k: int = None,
|
||||
top_p: float = None,
|
||||
tfs: float = None,
|
||||
temp: float = None,
|
||||
beams: int = 1,
|
||||
) -> LogitsProcessorList:
|
||||
def new_get_logits_warper(beams: int = 1,) -> LogitsProcessorList:
|
||||
warper_list = LogitsProcessorList()
|
||||
if(top_k is not None and top_k > 0):
|
||||
warper_list.append(TopKLogitsWarper(top_k=top_k, min_tokens_to_keep=1 + (beams > 1)))
|
||||
if(top_p is not None and top_p < 1.0):
|
||||
warper_list.append(TopPLogitsWarper(top_p=top_p, min_tokens_to_keep=1 + (beams > 1)))
|
||||
if(tfs is not None and tfs < 1.0):
|
||||
warper_list.append(TailFreeLogitsWarper(tfs=tfs, min_tokens_to_keep=1 + (beams > 1)))
|
||||
if(temp is not None and temp != 1.0):
|
||||
warper_list.append(TemperatureLogitsWarper(temperature=temp))
|
||||
warper_list.append(TopKLogitsWarper(top_k=1, min_tokens_to_keep=1 + (beams > 1)))
|
||||
warper_list.append(TopPLogitsWarper(top_p=0.5, min_tokens_to_keep=1 + (beams > 1)))
|
||||
warper_list.append(TailFreeLogitsWarper(tfs=0.5, min_tokens_to_keep=1 + (beams > 1)))
|
||||
warper_list.append(TemperatureLogitsWarper(temperature=0.5))
|
||||
return warper_list
|
||||
|
||||
def new_sample(self, *args, **kwargs):
|
||||
assert kwargs.pop("logits_warper", None) is not None
|
||||
kwargs["logits_warper"] = new_get_logits_warper(
|
||||
top_k=vars.top_k,
|
||||
top_p=vars.top_p,
|
||||
tfs=vars.tfs,
|
||||
temp=vars.temp,
|
||||
beams=1,
|
||||
)
|
||||
return new_sample.old_sample(self, *args, **kwargs)
|
||||
|
@ -1048,10 +1056,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)
|
||||
|
@ -1376,23 +1387,23 @@ def lua_get_modeltype():
|
|||
return "readonly"
|
||||
if(vars.model in ("Colab", "OAI", "InferKit")):
|
||||
return "api"
|
||||
if(vars.model in ("GPT2Custom", "NeoCustom")):
|
||||
if(vars.model not in ("TPUMeshTransformerGPTJ",) and (vars.model in ("GPT2Custom", "NeoCustom") or vars.model_type in ("gpt2", "gpt_neo", "gptj"))):
|
||||
hidden_size = get_hidden_size_from_model(model)
|
||||
if(vars.model in ("gpt2",) or (vars.model == "GPT2Custom" and hidden_size == 768)):
|
||||
if(vars.model in ("gpt2",) or (vars.model_type == "gpt2" and hidden_size == 768)):
|
||||
return "gpt2"
|
||||
if(vars.model in ("gpt2-medium",) or (vars.model == "GPT2Custom" and hidden_size == 1024)):
|
||||
if(vars.model in ("gpt2-medium",) or (vars.model_type == "gpt2" and hidden_size == 1024)):
|
||||
return "gpt2-medium"
|
||||
if(vars.model in ("gpt2-large",) or (vars.model == "GPT2Custom" and hidden_size == 1280)):
|
||||
if(vars.model in ("gpt2-large",) or (vars.model_type == "gpt2" and hidden_size == 1280)):
|
||||
return "gpt2-large"
|
||||
if(vars.model in ("gpt2-xl",) or (vars.model == "GPT2Custom" and hidden_size == 1600)):
|
||||
if(vars.model in ("gpt2-xl",) or (vars.model_type == "gpt2" and hidden_size == 1600)):
|
||||
return "gpt2-xl"
|
||||
if(vars.model == "NeoCustom" and hidden_size == 768):
|
||||
if(vars.model_type == "gpt_neo" and hidden_size == 768):
|
||||
return "gpt-neo-125M"
|
||||
if(vars.model in ("EleutherAI/gpt-neo-1.3B",) or (vars.model == "NeoCustom" and hidden_size == 2048)):
|
||||
if(vars.model in ("EleutherAI/gpt-neo-1.3B",) or (vars.model_type == "gpt_neo" and hidden_size == 2048)):
|
||||
return "gpt-neo-1.3B"
|
||||
if(vars.model in ("EleutherAI/gpt-neo-2.7B",) or (vars.model == "NeoCustom" and hidden_size == 2560)):
|
||||
if(vars.model in ("EleutherAI/gpt-neo-2.7B",) or (vars.model_type == "gpt_neo" and hidden_size == 2560)):
|
||||
return "gpt-neo-2.7B"
|
||||
if(vars.model in ("EleutherAI/gpt-j-6B",) or (vars.model == "NeoCustom" and hidden_size == 4096) or (vars.model == "TPUMeshTransformerGPTJ" and tpu_mtj_backend.params["d_model"] == 4096)):
|
||||
if(vars.model in ("EleutherAI/gpt-j-6B",) or (vars.model == "TPUMeshTransformerGPTJ" and tpu_mtj_backend.params["d_model"] == 4096) or (vars.model_type in ("gpt_neo", "gptj") and hidden_size == 4096)):
|
||||
return "gpt-j-6B"
|
||||
return "unknown"
|
||||
|
||||
|
@ -1423,7 +1434,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)
|
||||
|
@ -1439,7 +1452,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)
|
||||
|
@ -1465,7 +1480,8 @@ vars.lua_state = lupa.LuaRuntime(unpack_returned_tuples=True)
|
|||
bridged = {
|
||||
"corescript_path": os.path.join(os.path.dirname(os.path.realpath(__file__)), "cores"),
|
||||
"userscript_path": os.path.join(os.path.dirname(os.path.realpath(__file__)), "userscripts"),
|
||||
"lib_path": os.path.join(os.path.dirname(os.path.realpath(__file__)), "extern", "lualibs"),
|
||||
"config_path": os.path.join(os.path.dirname(os.path.realpath(__file__)), "userscripts"),
|
||||
"lib_paths": vars.lua_state.table(os.path.join(os.path.dirname(os.path.realpath(__file__)), "lualibs"), os.path.join(os.path.dirname(os.path.realpath(__file__)), "extern", "lualibs")),
|
||||
"load_callback": load_callback,
|
||||
"print": lua_print,
|
||||
"warn": lua_warn,
|
||||
|
@ -1523,7 +1539,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.spfilename: vars.spmeta} if vars.allowsp and len(vars.spfilename) else {}}, broadcast=True)
|
||||
|
||||
if(not vars.gamestarted):
|
||||
setStartState()
|
||||
sendsettings()
|
||||
|
@ -1714,6 +1733,12 @@ def get_message(msg):
|
|||
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'] == 'wifoldercollapsecontent'):
|
||||
vars.wifolders_d[msg['data']]['collapsed'] = True
|
||||
emit('from_server', {'cmd': 'wifoldercollapsecontent', 'data': msg['data']}, broadcast=True)
|
||||
elif(msg['cmd'] == 'wifolderexpandcontent'):
|
||||
vars.wifolders_d[msg['data']]['collapsed'] = False
|
||||
emit('from_server', {'cmd': 'wifolderexpandcontent', 'data': msg['data']}, broadcast=True)
|
||||
elif(msg['cmd'] == 'wiupdate'):
|
||||
num = int(msg['num'])
|
||||
fields = ("key", "keysecondary", "content", "comment")
|
||||
|
@ -1753,7 +1778,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']:
|
||||
|
@ -1765,8 +1791,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'):
|
||||
|
@ -1775,6 +1801,7 @@ def get_message(msg):
|
|||
loadRequest(fileops.storypath(vars.loadselect))
|
||||
elif(msg['cmd'] == 'sprequest'):
|
||||
spRequest(vars.spselect)
|
||||
emit('from_server', {'cmd': 'spstatitems', 'data': {vars.spfilename: vars.spmeta} if vars.allowsp and len(vars.spfilename) else {}}, broadcast=True)
|
||||
elif(msg['cmd'] == 'deletestory'):
|
||||
deletesave(msg['data'])
|
||||
elif(msg['cmd'] == 'renamestory'):
|
||||
|
@ -1812,7 +1839,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
|
||||
#==================================================================#
|
||||
|
@ -1870,6 +1907,7 @@ def savesettings():
|
|||
|
||||
js["userscripts"] = vars.userscripts
|
||||
js["corescript"] = vars.corescript
|
||||
js["softprompt"] = vars.spfilename
|
||||
|
||||
# Write it
|
||||
if not os.path.exists('settings'):
|
||||
|
@ -1939,6 +1977,11 @@ def loadsettings():
|
|||
else:
|
||||
vars.corescript = "default.lua"
|
||||
|
||||
if(vars.allowsp and "softprompt" in js and type(js["softprompt"]) is str and all(q not in js["softprompt"] for q in ("..", ":")) and all(js["softprompt"][0] not in q for q in ("/", "\\"))):
|
||||
spRequest(js["softprompt"])
|
||||
else:
|
||||
vars.spfilename = ""
|
||||
|
||||
file.close()
|
||||
|
||||
#==================================================================#
|
||||
|
@ -2445,7 +2488,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)
|
||||
|
@ -2672,7 +2717,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)
|
||||
|
@ -3550,7 +3597,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())}
|
||||
|
@ -3558,11 +3605,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
|
||||
|
@ -3684,6 +3734,9 @@ def loadRequest(loadpath, filename=None):
|
|||
# Load a soft prompt from a file
|
||||
#==================================================================#
|
||||
def spRequest(filename):
|
||||
vars.spfilename = ""
|
||||
settingschanged()
|
||||
|
||||
if(len(filename) == 0):
|
||||
vars.sp = None
|
||||
vars.sp_length = 0
|
||||
|
@ -3695,6 +3748,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:
|
||||
|
@ -3725,6 +3780,9 @@ def spRequest(filename):
|
|||
else:
|
||||
vars.sp = torch.from_numpy(tensor)
|
||||
|
||||
vars.spfilename = filename
|
||||
settingschanged()
|
||||
|
||||
#==================================================================#
|
||||
# Import an AIDungon game exported with Mimi's tool
|
||||
#==================================================================#
|
||||
|
|
226
bridge.lua
226
bridge.lua
|
@ -3,7 +3,7 @@
|
|||
|
||||
---@param _python? table<string, any>
|
||||
---@param _bridged? table<string, any>
|
||||
---@return KoboldLib, KoboldCoreLib|nil
|
||||
---@return KoboldLib, KoboldCoreLib?
|
||||
return function(_python, _bridged)
|
||||
|
||||
--==========================================================================
|
||||
|
@ -14,7 +14,7 @@ return function(_python, _bridged)
|
|||
---@generic K, V
|
||||
---@param t table<K, V>
|
||||
---@param k? K
|
||||
---@return K|nil, V|nil
|
||||
---@return K?, V?
|
||||
function next(t, k)
|
||||
local meta = getmetatable(t)
|
||||
return ((meta ~= nil and type(rawget(t, "_name")) == "string" and string.match(rawget(t, "_name"), "^Kobold") and type(meta._kobold_next) == "function") and meta._kobold_next or old_next)(t, k)
|
||||
|
@ -40,23 +40,30 @@ return function(_python, _bridged)
|
|||
return original
|
||||
end
|
||||
|
||||
---@param path string
|
||||
---@param paths string|table<integer, string>
|
||||
---@return nil
|
||||
function set_require_path(path)
|
||||
local function set_require_path(paths)
|
||||
if type(paths) == "string" then
|
||||
paths = {paths}
|
||||
end
|
||||
local config = {}
|
||||
local i = 1
|
||||
for substring in string.gmatch(package.config, "[^\n]+") do
|
||||
config[i] = substring
|
||||
i = i + 1
|
||||
end
|
||||
package.path = path .. config[1] .. config[3] .. ".lua" .. config[2] .. path .. config[1] .. config[3] .. config[1] .. "init.lua"
|
||||
local _paths = {}
|
||||
for i, path in ipairs(paths) do
|
||||
_paths[i] = path .. config[1] .. config[3] .. ".lua" .. config[2] .. path .. config[1] .. config[3] .. config[1] .. "init.lua"
|
||||
end
|
||||
package.path = table.concat(_paths, config[2])
|
||||
package.cpath = ""
|
||||
end
|
||||
|
||||
---@param path string
|
||||
---@param filename string
|
||||
---@return string
|
||||
function join_folder_and_filename(path, filename)
|
||||
local function join_folder_and_filename(path, filename)
|
||||
return path .. string.match(package.config, "[^\n]+") .. filename
|
||||
end
|
||||
|
||||
|
@ -67,10 +74,10 @@ return function(_python, _bridged)
|
|||
|
||||
local bridged = {}
|
||||
for k in _python.iter(_bridged) do
|
||||
v = _bridged[k]
|
||||
local v = _bridged[k]
|
||||
bridged[k] = type(v) == "userdata" and _python.as_attrgetter(v) or v
|
||||
end
|
||||
set_require_path(bridged.lib_path)
|
||||
set_require_path(bridged.lib_paths)
|
||||
|
||||
|
||||
--==========================================================================
|
||||
|
@ -120,12 +127,12 @@ return function(_python, _bridged)
|
|||
_needs_unwrap = true
|
||||
wrapped = true
|
||||
end
|
||||
local r = {wrapped_func(...)}
|
||||
local r = table.pack(wrapped_func(...))
|
||||
if _needs_unwrap then
|
||||
metatables:restore()
|
||||
wrapped = false
|
||||
end
|
||||
return table.unpack(r)
|
||||
return table.unpack(r, 1, r.n)
|
||||
end)
|
||||
else
|
||||
return rawset(t, k, wrapped_func)
|
||||
|
@ -153,6 +160,8 @@ return function(_python, _bridged)
|
|||
---@field generated_cols integer
|
||||
---@field outputs table<integer, string>
|
||||
---@field num_outputs integer
|
||||
---@field feedback string
|
||||
---@field is_config_file_open boolean
|
||||
local kobold = setmetatable({}, metawrapper)
|
||||
local KoboldLib_mt = setmetatable({}, metawrapper)
|
||||
local KoboldLib_getters = setmetatable({}, metawrapper)
|
||||
|
@ -214,7 +223,12 @@ return function(_python, _bridged)
|
|||
koboldbridge.generated = {}
|
||||
koboldbridge.generated_cols = 0
|
||||
koboldbridge.outputs = {}
|
||||
koboldbridge.feedback = nil ---@type string|nil
|
||||
koboldbridge.feedback = nil ---@type string?
|
||||
|
||||
function koboldbridge:clear_userscript_metadata()
|
||||
self.logging_name = nil
|
||||
self.filename = nil
|
||||
end
|
||||
|
||||
---@return nil
|
||||
local function maybe_require_regeneration()
|
||||
|
@ -224,6 +238,84 @@ return function(_python, _bridged)
|
|||
end
|
||||
|
||||
|
||||
--==========================================================================
|
||||
-- Userscript API: Configuration
|
||||
--==========================================================================
|
||||
|
||||
local config_files = {} ---@type table<string, file*>
|
||||
local config_file_filename_map = {} ---@type table<file*, string>
|
||||
|
||||
---@return file*?
|
||||
local function open_and_handle_errors(...)
|
||||
local file, err_msg = io.open(...)
|
||||
if err_msg ~= nil then
|
||||
koboldbridge.obliterate_multiverse()
|
||||
error(err_msg)
|
||||
return
|
||||
end
|
||||
return file
|
||||
end
|
||||
|
||||
---@param file? file*
|
||||
local function new_close_pre(file)
|
||||
if file == nil then
|
||||
file = io.output()
|
||||
end
|
||||
local filename = config_file_filename_map[file]
|
||||
if filename ~= nil then
|
||||
config_file_filename_map[file] = nil
|
||||
config_files[filename] = nil
|
||||
end
|
||||
end
|
||||
|
||||
---@param f fun(file?: file*)
|
||||
local function _new_close(f)
|
||||
---@param file? file*
|
||||
return function(file)
|
||||
new_close_pre(file)
|
||||
return f(file)
|
||||
end
|
||||
end
|
||||
debug.getmetatable(io.stdout).__index.close = _new_close(io.stdout.close)
|
||||
|
||||
---@param filename string
|
||||
---@return boolean
|
||||
local function is_config_file_open(filename)
|
||||
return config_files[filename] ~= nil
|
||||
end
|
||||
|
||||
---@param filename string
|
||||
---@param clear? boolean
|
||||
---@return file*
|
||||
local function get_config_file(filename, clear)
|
||||
if not is_config_file_open(filename) then
|
||||
local config_filepath = join_folder_and_filename(bridged.config_path, filename .. ".conf")
|
||||
open_and_handle_errors(config_filepath, "a"):close()
|
||||
config_files[filename] = open_and_handle_errors(config_filepath, clear and "w+b" or "r+b")
|
||||
config_file_filename_map[config_files[filename]] = filename
|
||||
end
|
||||
return config_files[filename]
|
||||
end
|
||||
|
||||
---@param clear? boolean
|
||||
---@return file*
|
||||
function kobold.get_config_file(clear)
|
||||
return get_config_file(koboldbridge.filename, clear)
|
||||
end
|
||||
|
||||
---@param t KoboldLib
|
||||
---@return boolean
|
||||
function KoboldLib_getters.is_config_file_open(t)
|
||||
return is_config_file_open(koboldbridge.filename)
|
||||
end
|
||||
|
||||
---@param t KoboldLib
|
||||
---@param v boolean
|
||||
function KoboldLib_setters.is_config_file_open(t, v)
|
||||
error("`KoboldLib.is_config_file_open` is a read-only attribute")
|
||||
end
|
||||
|
||||
|
||||
--==========================================================================
|
||||
-- Userscript API: World Info
|
||||
--==========================================================================
|
||||
|
@ -373,7 +465,7 @@ return function(_python, _bridged)
|
|||
local KoboldWorldInfoFolder_mt = setmetatable({}, metawrapper)
|
||||
|
||||
---@param u integer
|
||||
---@return KoboldWorldInfoEntry|nil
|
||||
---@return KoboldWorldInfoEntry?
|
||||
function KoboldWorldInfoFolder:finduid(u)
|
||||
if not check_validity(self) or type(u) ~= "number" then
|
||||
return
|
||||
|
@ -440,7 +532,7 @@ return function(_python, _bridged)
|
|||
KoboldWorldInfoFolder_mt.__pairs = KoboldWorldInfoEntry_mt.__pairs
|
||||
|
||||
---@param t KoboldWorldInfoFolder|KoboldWorldInfo
|
||||
---@return KoboldWorldInfoEntry|nil
|
||||
---@return KoboldWorldInfoEntry?
|
||||
function KoboldWorldInfoFolder_mt.__index(t, k)
|
||||
if not check_validity(t) then
|
||||
return
|
||||
|
@ -495,7 +587,7 @@ return function(_python, _bridged)
|
|||
local KoboldWorldInfoFolderSelector_mt = setmetatable({}, metawrapper)
|
||||
|
||||
---@param u integer
|
||||
---@return KoboldWorldInfoFolder|nil
|
||||
---@return KoboldWorldInfoFolder?
|
||||
function KoboldWorldInfoFolderSelector:finduid(u)
|
||||
if not check_validity(self) or type(u) ~= "number" then
|
||||
return
|
||||
|
@ -528,7 +620,7 @@ return function(_python, _bridged)
|
|||
KoboldWorldInfoFolderSelector_mt.__pairs = KoboldWorldInfoEntry_mt.__pairs
|
||||
|
||||
---@param t KoboldWorldInfoFolderSelector
|
||||
---@return KoboldWorldInfoFolder|nil
|
||||
---@return KoboldWorldInfoFolder?
|
||||
function KoboldWorldInfoFolderSelector_mt.__index(t, k)
|
||||
if not check_validity(t) or type(k) ~= "number" or math.tointeger(k) == nil or k < 1 or k > #t then
|
||||
return
|
||||
|
@ -681,7 +773,7 @@ return function(_python, _bridged)
|
|||
local actions = koboldbridge.userstate == "genmod" and bridged.vars._actions or bridged.vars.actions
|
||||
local nxt, iterator = _python.iter(actions)
|
||||
local run_once = false
|
||||
local f = function()
|
||||
local function f()
|
||||
if not bridged.vars.gamestarted then
|
||||
return
|
||||
end
|
||||
|
@ -709,7 +801,7 @@ return function(_python, _bridged)
|
|||
local actions = koboldbridge.userstate == "genmod" and bridged.vars._actions or bridged.vars.actions
|
||||
local nxt, iterator = _python.iter(_python.builtins.reversed(actions))
|
||||
local last_run = false
|
||||
local f = function()
|
||||
local function f()
|
||||
if not bridged.vars.gamestarted or last_run then
|
||||
return
|
||||
end
|
||||
|
@ -946,7 +1038,7 @@ return function(_python, _bridged)
|
|||
---@param t KoboldLib
|
||||
---@return string
|
||||
function KoboldLib_getters.model(t)
|
||||
return bridged.vars.model
|
||||
return bridged.vars.model_orig
|
||||
end
|
||||
|
||||
---@param t KoboldLib
|
||||
|
@ -1257,21 +1349,22 @@ return function(_python, _bridged)
|
|||
-- Core script API
|
||||
--==========================================================================
|
||||
|
||||
koboldbridge.userscripts = {} ---@type table<integer, string>
|
||||
koboldbridge.userscripts = {} ---@type table<integer, KoboldUserScriptModule>
|
||||
koboldbridge.userscriptmodule_filename_map = {} ---@type table<KoboldUserScriptModule, string>
|
||||
koboldbridge.num_userscripts = 0
|
||||
koboldbridge.inmod = nil ---@type function|nil
|
||||
koboldbridge.genmod = nil ---@type function|nil
|
||||
koboldbridge.outmod = nil ---@type function|nil
|
||||
koboldbridge.inmod = nil ---@type function?
|
||||
koboldbridge.genmod = nil ---@type function?
|
||||
koboldbridge.outmod = nil ---@type function?
|
||||
|
||||
---@class KoboldUserScript
|
||||
---@field inmod function|nil
|
||||
---@field genmod function|nil
|
||||
---@field outmod function|nil
|
||||
---@field inmod? function
|
||||
---@field genmod? function
|
||||
---@field outmod? function
|
||||
|
||||
---@class KoboldCoreScript
|
||||
---@field inmod function|nil
|
||||
---@field genmod function|nil
|
||||
---@field outmod function|nil
|
||||
---@field inmod? function
|
||||
---@field genmod? function
|
||||
---@field outmod? function
|
||||
|
||||
|
||||
----------------------------------------------------------------------------
|
||||
|
@ -1280,9 +1373,10 @@ return function(_python, _bridged)
|
|||
---@field filename string
|
||||
---@field modulename string
|
||||
---@field description string
|
||||
---@field inmod function|nil
|
||||
---@field genmod function|nil
|
||||
---@field outmod function|nil
|
||||
---@field is_config_file_open boolean
|
||||
---@field inmod? function
|
||||
---@field genmod? function
|
||||
---@field outmod? function
|
||||
local KoboldUserScriptModule = setmetatable({
|
||||
_name = "KoboldUserScriptModule",
|
||||
}, metawrapper)
|
||||
|
@ -1297,6 +1391,12 @@ return function(_python, _bridged)
|
|||
outmod = false,
|
||||
}
|
||||
|
||||
---@param clear? boolean
|
||||
---@return file*
|
||||
function KoboldUserScriptModule:get_config_file(clear)
|
||||
return get_config_file(koboldbridge.userscriptmodule_filename_map[self], clear)
|
||||
end
|
||||
|
||||
---@generic K
|
||||
---@param t KoboldUserScriptModule
|
||||
---@param k K
|
||||
|
@ -1316,6 +1416,8 @@ return function(_python, _bridged)
|
|||
function KoboldUserScriptModule_mt.__index(t, k)
|
||||
if type(k) == "string" and KoboldUserScriptModule_fields[k] ~= nil then
|
||||
return rawget(t, "_" .. k)
|
||||
elseif k == "is_config_file_open" then
|
||||
return is_config_file_open(koboldbridge.userscriptmodule_filename_map[t])
|
||||
end
|
||||
return rawget(t, k)
|
||||
end
|
||||
|
@ -1346,7 +1448,7 @@ return function(_python, _bridged)
|
|||
|
||||
---@param t KoboldUserScriptList
|
||||
---@param k integer
|
||||
---@return KoboldUserScriptModule|nil
|
||||
---@return KoboldUserScriptModule?
|
||||
function KoboldUserScriptList_mt.__index(t, k)
|
||||
if type(k) == "number" and math.tointeger(k) ~= nil then
|
||||
return koboldbridge.userscripts[k]
|
||||
|
@ -1405,6 +1507,7 @@ return function(_python, _bridged)
|
|||
|
||||
local envs = {}
|
||||
koboldbridge.logging_name = nil
|
||||
koboldbridge.filename = nil
|
||||
|
||||
local old_load = load
|
||||
local function _safe_load(_g)
|
||||
|
@ -1427,11 +1530,11 @@ return function(_python, _bridged)
|
|||
local old_package_searchers = package.searchers
|
||||
---@param modname string
|
||||
---@param env table<string, any>
|
||||
---@param search_path? string
|
||||
---@return any, string|nil
|
||||
local function requirex(modname, env, search_path)
|
||||
if search_path == nil then
|
||||
search_path = bridged.lib_path
|
||||
---@param search_paths? string|table<integer, string>
|
||||
---@return any, string?
|
||||
local function requirex(modname, env, search_paths)
|
||||
if search_paths == nil then
|
||||
search_paths = bridged.lib_paths
|
||||
end
|
||||
if modname == "bridge" then
|
||||
return function() return env.kobold, env.koboldcore end
|
||||
|
@ -1449,7 +1552,7 @@ return function(_python, _bridged)
|
|||
local loader, path
|
||||
local errors = {}
|
||||
local n_errors = 0
|
||||
set_require_path(search_path)
|
||||
set_require_path(search_paths)
|
||||
for k, v in ipairs(old_package_searchers) do
|
||||
loader, path = v(modname)
|
||||
if allowsearch and type(loader) == "function" then
|
||||
|
@ -1459,7 +1562,7 @@ return function(_python, _bridged)
|
|||
errors[n_errors] = "\n\t" .. loader
|
||||
end
|
||||
end
|
||||
set_require_path(bridged.lib_path)
|
||||
set_require_path(bridged.lib_paths)
|
||||
if not allowsearch or type(loader) ~= "function" then
|
||||
error("module '" .. modname .. "' not found:" .. table.concat(errors))
|
||||
return
|
||||
|
@ -1470,7 +1573,7 @@ return function(_python, _bridged)
|
|||
end
|
||||
local function _safe_require(_g)
|
||||
---@param modname string
|
||||
---@return any, string|nil
|
||||
---@return any, string?
|
||||
return function(modname)
|
||||
return requirex(modname, _g)
|
||||
end
|
||||
|
@ -1610,6 +1713,8 @@ return function(_python, _bridged)
|
|||
output = io.output,
|
||||
read = io.read,
|
||||
write = io.write,
|
||||
close = _new_close(io.close),
|
||||
lines = io.lines,
|
||||
flush = io.flush,
|
||||
type = io.type,
|
||||
},
|
||||
|
@ -1654,7 +1759,11 @@ return function(_python, _bridged)
|
|||
end
|
||||
|
||||
function koboldbridge.obliterate_multiverse()
|
||||
for k, v in pairs(config_files) do
|
||||
pcall(v.close, v)
|
||||
end
|
||||
envs = {}
|
||||
koboldbridge.userscripts = {}
|
||||
koboldbridge.num_userscripts = 0
|
||||
koboldbridge.inmod = nil
|
||||
koboldbridge.genmod = nil
|
||||
|
@ -1668,23 +1777,49 @@ return function(_python, _bridged)
|
|||
|
||||
---@return nil
|
||||
function koboldbridge.load_userscripts(filenames, modulenames, descriptions)
|
||||
set_require_path(bridged.userscript_path)
|
||||
config_files = {}
|
||||
config_file_filename_map = {}
|
||||
koboldbridge.userscripts = {}
|
||||
koboldbridge.userscriptmodule_filename_map = {}
|
||||
koboldbridge.num_userscripts = 0
|
||||
for i, filename in _python.enumerate(filenames) do
|
||||
bridged.load_callback(filename, modulenames[i])
|
||||
koboldbridge.logging_name = modulenames[i]
|
||||
koboldbridge.filename = filename
|
||||
---@type KoboldUserScript
|
||||
local _userscript = old_loadfile(join_folder_and_filename(bridged.userscript_path, filename), "t", koboldbridge.get_universe(filename))()
|
||||
koboldbridge.logging_name = nil
|
||||
koboldbridge.filename = nil
|
||||
local userscript = deepcopy(KoboldUserScriptModule)
|
||||
rawset(userscript, "_inmod", function() koboldbridge.logging_name = modulenames[i]; if _userscript.inmod ~= nil then _userscript.inmod() end end)
|
||||
rawset(userscript, "_genmod", function() koboldbridge.logging_name = modulenames[i]; if _userscript.genmod ~= nil then _userscript.genmod() end end)
|
||||
rawset(userscript, "_outmod", function() koboldbridge.logging_name = modulenames[i]; if _userscript.outmod ~= nil then _userscript.outmod() end end)
|
||||
rawset(userscript, "_inmod", function()
|
||||
koboldbridge.logging_name = modulenames[i]
|
||||
koboldbridge.filename = filename
|
||||
if _userscript.inmod ~= nil then
|
||||
_userscript.inmod()
|
||||
end
|
||||
koboldbridge:clear_userscript_metadata()
|
||||
end)
|
||||
rawset(userscript, "_genmod", function()
|
||||
koboldbridge.logging_name = modulenames[i]
|
||||
koboldbridge.filename = filename
|
||||
if _userscript.genmod ~= nil then
|
||||
_userscript.genmod()
|
||||
end
|
||||
koboldbridge:clear_userscript_metadata()
|
||||
end)
|
||||
rawset(userscript, "_outmod", function()
|
||||
koboldbridge.logging_name = modulenames[i]
|
||||
koboldbridge.filename = filename
|
||||
if _userscript.outmod ~= nil then
|
||||
_userscript.outmod()
|
||||
end
|
||||
koboldbridge:clear_userscript_metadata()
|
||||
end)
|
||||
rawset(userscript, "_filename", filename)
|
||||
rawset(userscript, "_modulename", modulenames[i])
|
||||
rawset(userscript, "_description", descriptions[i])
|
||||
koboldbridge.userscripts[i+1] = userscript
|
||||
koboldbridge.userscriptmodule_filename_map[userscript] = filename
|
||||
koboldbridge.num_userscripts = i + 1
|
||||
end
|
||||
end
|
||||
|
@ -1700,6 +1835,7 @@ return function(_python, _bridged)
|
|||
|
||||
function koboldbridge.execute_inmod()
|
||||
local r
|
||||
koboldbridge:clear_userscript_metadata()
|
||||
koboldbridge.restart_sequence = nil
|
||||
koboldbridge.userstate = "inmod"
|
||||
koboldbridge.regeneration_required = false
|
||||
|
@ -1722,6 +1858,7 @@ return function(_python, _bridged)
|
|||
---@return any, boolean
|
||||
function koboldbridge.execute_genmod()
|
||||
local r
|
||||
koboldbridge:clear_userscript_metadata()
|
||||
koboldbridge.generating = true
|
||||
koboldbridge.userstate = "genmod"
|
||||
if koboldbridge.genmod ~= nil then
|
||||
|
@ -1758,6 +1895,7 @@ return function(_python, _bridged)
|
|||
|
||||
function koboldbridge.execute_outmod()
|
||||
local r
|
||||
koboldbridge:clear_userscript_metadata()
|
||||
koboldbridge.generating = false
|
||||
koboldbridge.userstate = "outmod"
|
||||
koboldbridge.num_outputs = kobold.settings.numseqs
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
return require("lulpeg")
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -419,7 +419,7 @@ function addWiLine(ob) {
|
|||
function addWiFolder(uid, ob) {
|
||||
if(uid !== null) {
|
||||
var uninitialized = $("#wilistfoldercontainer"+null);
|
||||
var html = "<div class=\"wisortable-container\" id=\"wilistfoldercontainer"+uid+"\" folder-uid=\""+uid+"\">\
|
||||
var html = "<div class=\"wisortable-container "+(ob.collapsed ? "" : "folder-expanded")+"\" id=\"wilistfoldercontainer"+uid+"\" folder-uid=\""+uid+"\">\
|
||||
<div class=\"wilistfolder\" id=\"wilistfolder"+uid+"\">\
|
||||
<div class=\"wiremove\">\
|
||||
<button type=\"button\" class=\"btn btn-primary heightfull\" id=\"btn_wifolder"+uid+"\">X</button>\
|
||||
|
@ -428,7 +428,7 @@ function addWiFolder(uid, ob) {
|
|||
</div>\
|
||||
<div class=\"wifoldericon\">\
|
||||
<div class=\"wicentered\">\
|
||||
<span class=\"oi oi-folder\" aria-hidden=\"true\"></span>\
|
||||
<span class=\"oi oi-folder folder-expand "+(ob.collapsed ? "" : "folder-expanded")+"\" id=\"btn_wifolderexpand"+uid+"\" aria-hidden=\"true\"></span>\
|
||||
</div>\
|
||||
</div>\
|
||||
<div class=\"wifoldername\">\
|
||||
|
@ -461,7 +461,7 @@ function addWiFolder(uid, ob) {
|
|||
var onfocusout = function () {
|
||||
socket.send({'cmd': 'wifolderupdate', 'uid': uid, 'data': {
|
||||
name: $("#wifoldername"+uid).val(),
|
||||
collapsed: false,
|
||||
collapsed: !$("#btn_wifolderexpand"+uid).hasClass("folder-expanded"),
|
||||
}});
|
||||
};
|
||||
$("#wifoldergutter"+uid).on("click", function () {
|
||||
|
@ -488,9 +488,22 @@ function addWiFolder(uid, ob) {
|
|||
$(this).parent().parent().find(".wisortable-body").removeClass("hidden");
|
||||
$(this).parent().css("max-height", "").find(".wifoldername").find(".form-control").css("max-height", "");
|
||||
});
|
||||
$("#btn_wifolderexpand"+uid).on("click", function () {
|
||||
if($(this).hasClass("folder-expanded")) {
|
||||
socket.send({'cmd': 'wifoldercollapsecontent', 'data': uid});
|
||||
} else {
|
||||
socket.send({'cmd': 'wifolderexpandcontent', 'data': uid});
|
||||
}
|
||||
})
|
||||
adjustWiFolderNameHeight($("#wifoldername"+uid)[0]);
|
||||
if(ob.collapsed) {
|
||||
setTimeout(function() {
|
||||
var container = $("#wilistfoldercontainer"+uid);
|
||||
hide([container.find(".wifoldergutter-container"), container.find(".wisortable-body")]);
|
||||
}, 2);
|
||||
}
|
||||
} else {
|
||||
wi_menu.append("<div class=\"wisortable-container\" id=\"wilistfoldercontainer"+uid+"\">\
|
||||
wi_menu.append("<div class=\"wisortable-container folder-expanded\" id=\"wilistfoldercontainer"+uid+"\">\
|
||||
<div class=\"wilistfolder\" id=\"wilistfolder"+uid+"\">\
|
||||
<div class=\"wiremove\">\
|
||||
<button type=\"button\" class=\"btn btn-primary heightfull\" id=\"btn_wifolder"+uid+"\">+</button>\
|
||||
|
@ -499,7 +512,7 @@ function addWiFolder(uid, ob) {
|
|||
</div>\
|
||||
<div class=\"wifoldericon\">\
|
||||
<div class=\"wicentered\">\
|
||||
<span class=\"oi oi-folder\" aria-hidden=\"true\"></span>\
|
||||
<span class=\"oi oi-folder folder-expand folder-expanded\" id=\"btn_wifolderexpand"+uid+"\" aria-hidden=\"true\"></span>\
|
||||
</div>\
|
||||
</div>\
|
||||
<div class=\"wifoldername\">\
|
||||
|
@ -564,6 +577,18 @@ function hideWiFolderDeleteConfirm(num) {
|
|||
hide([$("#btn_wifolderdel"+num), $("#btn_wifoldercan"+num)]);
|
||||
}
|
||||
|
||||
function collapseWiFolderContent(uid) {
|
||||
hide([$("#wifoldergutter"+uid), $(".wisortable-body[folder-uid="+uid+"]")]);
|
||||
$("#btn_wifolderexpand"+uid).removeClass("folder-expanded");
|
||||
$("#wilistfoldercontainer"+uid).removeClass("folder-expanded");
|
||||
}
|
||||
|
||||
function expandWiFolderContent(uid) {
|
||||
show([$("#wifoldergutter"+uid), $(".wisortable-body[folder-uid="+uid+"]")]);
|
||||
$("#btn_wifolderexpand"+uid).addClass("folder-expanded");
|
||||
$("#wilistfoldercontainer"+uid).addClass("folder-expanded");
|
||||
}
|
||||
|
||||
function enableWiSelective(num) {
|
||||
hide([$("#wikey"+num)]);
|
||||
$("#wikeyprimary"+num).val($("#wikey"+num).val());
|
||||
|
@ -1028,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($("<li filename=\""+items[i].filename+"\">"+items[i].modulename+" <"+items[i].filename+"></li>"));
|
||||
}
|
||||
$("#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]);
|
||||
|
@ -1398,14 +1476,59 @@ function syncAllModifiedChunks(including_selected_chunks=false) {
|
|||
}
|
||||
|
||||
function restorePrompt() {
|
||||
if(game_text[0].firstChild && game_text[0].firstChild.nodeType === 3) {
|
||||
saved_prompt = formatChunkInnerText(game_text[0].firstChild);
|
||||
if($("#n0").length && formatChunkInnerText($("#n0")[0]).length === 0) {
|
||||
$("#n0").remove();
|
||||
}
|
||||
var shadow_text = $("<b>" + game_text.html() + "</b>");
|
||||
var detected = false;
|
||||
var ref = null;
|
||||
try {
|
||||
if(shadow_text.length && shadow_text[0].firstChild && (shadow_text[0].firstChild.nodeType === 3 || shadow_text[0].firstChild.tagName === "BR")) {
|
||||
detected = true;
|
||||
ref = shadow_text;
|
||||
} else if(game_text.length && game_text[0].firstChild && game_text[0].firstChild.nodeType === 3 || game_text[0].firstChild.tagName === "BR") {
|
||||
detected = true;
|
||||
ref = game_text;
|
||||
}
|
||||
} catch (e) {
|
||||
detected = false;
|
||||
}
|
||||
if(detected) {
|
||||
unbindGametext();
|
||||
game_text[0].innerText = "";
|
||||
var text = [];
|
||||
while(true) {
|
||||
if(ref.length && ref[0].firstChild && ref[0].firstChild.nodeType === 3) {
|
||||
text.push(ref[0].firstChild.textContent.replace(/\u00a0/g, " "));
|
||||
} else if(ref.length && ref[0].firstChild && ref[0].firstChild.tagName === "BR") {
|
||||
text.push("\n");
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
ref[0].removeChild(ref[0].firstChild);
|
||||
}
|
||||
text = text.join("").trim();
|
||||
if(text.length) {
|
||||
saved_prompt = text;
|
||||
}
|
||||
game_text[0].innerHTML = "";
|
||||
bindGametext();
|
||||
}
|
||||
if($("#n0").length) {
|
||||
$("#n0").remove();
|
||||
game_text.children().each(function() {
|
||||
if(this.tagName !== "CHUNK") {
|
||||
this.parentNode.removeChild(this);
|
||||
}
|
||||
});
|
||||
if(!detected) {
|
||||
game_text.children().each(function() {
|
||||
if(this.innerText.trim().length) {
|
||||
saved_prompt = this.innerText.trim();
|
||||
socket.send({'cmd': 'inlinedelete', 'data': this.getAttribute("n")});
|
||||
this.parentNode.removeChild(this);
|
||||
return false;
|
||||
}
|
||||
socket.send({'cmd': 'inlinedelete', 'data': this.getAttribute("n")});
|
||||
this.parentNode.removeChild(this);
|
||||
});
|
||||
}
|
||||
var prompt_chunk = document.createElement("chunk");
|
||||
prompt_chunk.setAttribute("n", "0");
|
||||
|
@ -1880,6 +2003,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);
|
||||
|
@ -1922,6 +2049,10 @@ $(document).ready(function(){
|
|||
expandWiLine(msg.data);
|
||||
} else if(msg.cmd == "wiexpandfolder") {
|
||||
expandWiFolderLine(msg.data);
|
||||
} else if(msg.cmd == "wifoldercollapsecontent") {
|
||||
collapseWiFolderContent(msg.data);
|
||||
} else if(msg.cmd == "wifolderexpandcontent") {
|
||||
expandWiFolderContent(msg.data);
|
||||
} else if(msg.cmd == "wiselon") {
|
||||
enableWiSelective(msg.data);
|
||||
} else if(msg.cmd == "wiseloff") {
|
||||
|
@ -2031,6 +2162,8 @@ $(document).ready(function(){
|
|||
connect_status.html("<b>Lost connection...</b>");
|
||||
connect_status.removeClass("color_green");
|
||||
connect_status.addClass("color_orange");
|
||||
updateUSStatItems([], false);
|
||||
updateSPStatItems({});
|
||||
});
|
||||
|
||||
// Register editing events
|
||||
|
@ -2062,6 +2195,7 @@ $(document).ready(function(){
|
|||
|
||||
// Make the userscripts menu sortable
|
||||
var us_sortable_settings = {
|
||||
placeholder: "ussortable-placeholder",
|
||||
delay: 2,
|
||||
cursor: "move",
|
||||
tolerance: "pointer",
|
||||
|
@ -2205,6 +2339,7 @@ $(document).ready(function(){
|
|||
});
|
||||
|
||||
load_accept.on("click", function(ev) {
|
||||
hideMessage();
|
||||
newly_loaded = true;
|
||||
socket.send({'cmd': 'loadrequest', 'data': ''});
|
||||
hideLoadPopup();
|
||||
|
@ -2215,6 +2350,7 @@ $(document).ready(function(){
|
|||
});
|
||||
|
||||
sp_accept.on("click", function(ev) {
|
||||
hideMessage();
|
||||
socket.send({'cmd': 'sprequest', 'data': ''});
|
||||
hideSPPopup();
|
||||
});
|
||||
|
@ -2225,6 +2361,7 @@ $(document).ready(function(){
|
|||
});
|
||||
|
||||
us_accept.on("click", function(ev) {
|
||||
hideMessage();
|
||||
socket.send({'cmd': 'usloaded', 'data': usloaded.find(".uslistitem").map(function() { return $(this).attr("name"); }).toArray()});
|
||||
socket.send({'cmd': 'usload', 'data': ''});
|
||||
hideUSPopup();
|
||||
|
@ -2235,6 +2372,7 @@ $(document).ready(function(){
|
|||
});
|
||||
|
||||
ns_accept.on("click", function(ev) {
|
||||
hideMessage();
|
||||
socket.send({'cmd': 'newgame', 'data': ''});
|
||||
hideNewStoryPopup();
|
||||
});
|
||||
|
@ -2267,6 +2405,7 @@ $(document).ready(function(){
|
|||
});
|
||||
|
||||
rs_accept.on("click", function(ev) {
|
||||
hideMessage();
|
||||
socket.send({'cmd': 'rndgame', 'data': topic.val()});
|
||||
hideRandomStoryPopup();
|
||||
});
|
||||
|
|
|
@ -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;
|
||||
|
@ -603,7 +606,12 @@ chunk.editing, chunk.editing * {
|
|||
background-color: #3bf723;
|
||||
}
|
||||
|
||||
.wisortable-container {
|
||||
.ussortable-placeholder {
|
||||
height: 4px;
|
||||
background-color: #3bf723;
|
||||
}
|
||||
|
||||
.wisortable-container.folder-expanded {
|
||||
padding-bottom: 50px;
|
||||
}
|
||||
|
||||
|
@ -651,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;
|
||||
|
@ -667,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 {
|
||||
|
@ -702,6 +775,23 @@ chunk.editing, chunk.editing * {
|
|||
position: relative;
|
||||
}
|
||||
|
||||
.folder-expand {
|
||||
opacity: 20%;
|
||||
}
|
||||
|
||||
.folder-expand:hover {
|
||||
opacity: 44%;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.folder-expand.folder-expanded {
|
||||
opacity: 80% !important;
|
||||
}
|
||||
|
||||
.folder-expand.folder-expanded:hover {
|
||||
opacity: 100% !important;
|
||||
}
|
||||
|
||||
.selective-key-icon {
|
||||
position: absolute !important;
|
||||
top: 5px !important;
|
||||
|
@ -893,7 +983,7 @@ chunk.editing, chunk.editing * {
|
|||
}
|
||||
|
||||
.navcontainer {
|
||||
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.nowrap {
|
||||
|
|
|
@ -10,12 +10,12 @@
|
|||
<script src="static/bootstrap.min.js"></script>
|
||||
<script src="static/bootstrap-toggle.min.js"></script>
|
||||
<script src="static/rangy-core.min.js"></script>
|
||||
<script src="static/application.js?ver=1.16.4i"></script>
|
||||
<script src="static/application.js?ver=1.16.4m"></script>
|
||||
|
||||
<link rel="stylesheet" href="static/jquery-ui.sortable.min.css">
|
||||
<link rel="stylesheet" href="static/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="static/bootstrap-toggle.min.css">
|
||||
<link rel="stylesheet" href="static/custom.css?ver=1.16.4e">
|
||||
<link rel="stylesheet" href="static/custom.css?ver=1.16.4g">
|
||||
<link rel="stylesheet" href="static/open-iconic-bootstrap.min.css">
|
||||
</head>
|
||||
<body>
|
||||
|
@ -80,8 +80,23 @@
|
|||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
<div id="connectstatusdiv">
|
||||
<span id="connectstatus" class="color_orange">Waiting for connection...</span>
|
||||
<div id="connectstatusdiv" class="flex-row-container">
|
||||
<span id="connectstatus" class="color_orange flex-row">Waiting for connection...</span>
|
||||
<div class="layer-container status-container flex-push-right">
|
||||
<span class="oi oi-puzzle-piece statusicon layer-bottom" aria-hidden="true">
|
||||
<div class="statustext statustext-wide">
|
||||
<div id="stat-us" class="statusheader">No userscripts active</div>
|
||||
<div id="stat-usactive"></div>
|
||||
</div>
|
||||
</span>
|
||||
<div class="layer-top statusiconlabel" id="usiconlabel"></div>
|
||||
</div>
|
||||
<span class="oi oi-lightbulb statusicon" aria-hidden="true">
|
||||
<div class="statustext">
|
||||
<div id="stat-sp" class="statusheader">No soft prompt active</div>
|
||||
<div id="stat-spactive"></div>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
Loading…
Reference in New Issue