Merge pull request #74 from ebolam/united
Redo, Pinning, and docker enhancements
This commit is contained in:
commit
e1ef4e4fa8
257
aiserver.py
257
aiserver.py
|
@ -121,6 +121,12 @@ class vars:
|
|||
setauthornotetemplate = authornotetemplate # Saved author's note template in settings
|
||||
andepth = 3 # How far back in history to append author's note
|
||||
actions = structures.KoboldStoryRegister() # Actions submitted by user and AI
|
||||
actions_metadata = [] # List of dictonaries, one dictonary for every action that contains information about the action like alternative options.
|
||||
# Contains at least the same number of items as actions. Back action will remove an item from actions, but not actions_metadata
|
||||
# Dictonary keys are:
|
||||
# Selected Text: (text the user had selected. None when this is a newly generated action)
|
||||
# Alternative Generated Text: {Text, Pinned, Previous Selection, Edited}
|
||||
#
|
||||
worldinfo = [] # List of World Info key/value objects
|
||||
worldinfo_i = [] # List of World Info key/value objects sans uninitialized entries
|
||||
worldinfo_u = {} # Dictionary of World Info UID - key/value pairs
|
||||
|
@ -202,6 +208,8 @@ class vars:
|
|||
nogenmod = False
|
||||
welcome = False # Custom Welcome Text (False is default)
|
||||
newlinemode = "n"
|
||||
quiet = False # If set will suppress any story text from being printed to the console (will only be seen on the client web page)
|
||||
debug = False # If set to true, will send debug information to the client for display
|
||||
|
||||
#==================================================================#
|
||||
# Function to get model selection at startup
|
||||
|
@ -465,6 +473,8 @@ 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.")
|
||||
parser.add_argument("--colab", action='store_true', help="Optimize for Google Colab.")
|
||||
parser.add_argument("--nobreakmodel", action='store_true', help="Disables Breakmodel support completely.")
|
||||
parser.add_argument("--share", action='store_true', default=False, help="If present will launch KoboldAI available to all computers rather than local only")
|
||||
parser.add_argument("--quiet", action='store_true', default=False, help="If present will suppress any story related text from showing on the console")
|
||||
|
||||
args: argparse.Namespace = None
|
||||
if(os.environ.get("KOBOLDAI_ARGS") is not None):
|
||||
|
@ -475,6 +485,9 @@ else:
|
|||
|
||||
vars.model = args.model;
|
||||
|
||||
if args.quiet:
|
||||
vars.quiet = True
|
||||
|
||||
if args.colab:
|
||||
args.remote = True;
|
||||
args.override_rename = True;
|
||||
|
@ -972,43 +985,50 @@ if(not vars.model in ["InferKit", "Colab", "OAI", "ReadOnly", "TPUMeshTransforme
|
|||
lowmem = {}
|
||||
|
||||
# Download model from Huggingface if it does not exist, otherwise load locally
|
||||
|
||||
#If we specify a model and it's in the root directory, we need to move it to the models directory (legacy folder structure to new)
|
||||
if os.path.isdir(vars.model.replace('/', '_')):
|
||||
import shutil
|
||||
shutil.move(vars.model.replace('/', '_'), "models/{}".format(vars.model.replace('/', '_')))
|
||||
if(os.path.isdir(vars.custmodpth)):
|
||||
with(maybe_use_float16()):
|
||||
try:
|
||||
tokenizer = AutoTokenizer.from_pretrained(vars.custmodpth, cache_dir="cache/")
|
||||
tokenizer = AutoTokenizer.from_pretrained(vars.custmodpth, cache_dir="cache")
|
||||
except ValueError as e:
|
||||
tokenizer = GPT2TokenizerFast.from_pretrained(vars.custmodpth, cache_dir="cache/")
|
||||
tokenizer = GPT2TokenizerFast.from_pretrained(vars.custmodpth, cache_dir="cache")
|
||||
try:
|
||||
model = AutoModelForCausalLM.from_pretrained(vars.custmodpth, cache_dir="cache/", **lowmem)
|
||||
model = AutoModelForCausalLM.from_pretrained(vars.custmodpth, cache_dir="cache", **lowmem)
|
||||
except ValueError as e:
|
||||
model = GPTNeoForCausalLM.from_pretrained(vars.custmodpth, cache_dir="cache/", **lowmem)
|
||||
elif(os.path.isdir(vars.model.replace('/', '_'))):
|
||||
model = GPTNeoForCausalLM.from_pretrained(vars.custmodpth, cache_dir="cache", **lowmem)
|
||||
elif(os.path.isdir("models/{}".format(vars.model.replace('/', '_')))):
|
||||
with(maybe_use_float16()):
|
||||
try:
|
||||
tokenizer = AutoTokenizer.from_pretrained(vars.model.replace('/', '_'), cache_dir="cache/")
|
||||
tokenizer = AutoTokenizer.from_pretrained("models/{}".format(vars.model.replace('/', '_')), cache_dir="cache")
|
||||
except ValueError as e:
|
||||
tokenizer = GPT2TokenizerFast.from_pretrained(vars.model.replace('/', '_'), cache_dir="cache/")
|
||||
tokenizer = GPT2TokenizerFast.from_pretrained("models/{}".format(vars.model.replace('/', '_')), cache_dir="cache")
|
||||
try:
|
||||
model = AutoModelForCausalLM.from_pretrained(vars.model.replace('/', '_'), cache_dir="cache/", **lowmem)
|
||||
model = AutoModelForCausalLM.from_pretrained("models/{}".format(vars.model.replace('/', '_')), cache_dir="cache", **lowmem)
|
||||
except ValueError as e:
|
||||
model = GPTNeoForCausalLM.from_pretrained(vars.model.replace('/', '_'), cache_dir="cache/", **lowmem)
|
||||
model = GPTNeoForCausalLM.from_pretrained("models/{}".format(vars.model.replace('/', '_')), cache_dir="cache", **lowmem)
|
||||
else:
|
||||
try:
|
||||
tokenizer = AutoTokenizer.from_pretrained(vars.model, cache_dir="cache/")
|
||||
tokenizer = AutoTokenizer.from_pretrained(vars.model, cache_dir="cache")
|
||||
except ValueError as e:
|
||||
tokenizer = GPT2TokenizerFast.from_pretrained(vars.model, cache_dir="cache/")
|
||||
tokenizer = GPT2TokenizerFast.from_pretrained(vars.model, cache_dir="cache")
|
||||
with(maybe_use_float16()):
|
||||
try:
|
||||
model = AutoModelForCausalLM.from_pretrained(vars.model, cache_dir="cache/", **lowmem)
|
||||
model = AutoModelForCausalLM.from_pretrained(vars.model, cache_dir="cache", **lowmem)
|
||||
except ValueError as e:
|
||||
model = GPTNeoForCausalLM.from_pretrained(vars.model, cache_dir="cache/", **lowmem)
|
||||
model = GPTNeoForCausalLM.from_pretrained(vars.model, cache_dir="cache", **lowmem)
|
||||
|
||||
if not args.colab:
|
||||
model = model.half()
|
||||
print("Trying to save model")
|
||||
import shutil
|
||||
shutil.rmtree("cache/")
|
||||
model.save_pretrained(vars.model.replace('/', '_'))
|
||||
tokenizer.save_pretrained(vars.model.replace('/', '_'))
|
||||
model = model.half()
|
||||
model.save_pretrained("models/{}".format(vars.model.replace('/', '_')))
|
||||
tokenizer.save_pretrained("models/{}".format(vars.model.replace('/', '_')))
|
||||
print("Saved")
|
||||
|
||||
if(vars.hascuda):
|
||||
if(vars.usegpu):
|
||||
|
@ -1173,6 +1193,7 @@ def download():
|
|||
js["authorsnote"] = vars.authornote
|
||||
js["anotetemplate"] = vars.authornotetemplate
|
||||
js["actions"] = tuple(vars.actions.values())
|
||||
js["actions_metadata"] = vars.actions_metadata
|
||||
js["worldinfo"] = []
|
||||
|
||||
# Extract only the important bits of WI
|
||||
|
@ -1637,7 +1658,11 @@ def lua_set_chunk(k, v):
|
|||
del vars._actions[chunk-1]
|
||||
vars.lua_deleted.add(chunk)
|
||||
if(not hasattr(vars, "_actions") or vars._actions is not vars.actions):
|
||||
del vars.actions[chunk-1]
|
||||
#Instead of deleting we'll blank out the text. This way our actions and actions_metadata stay in sync and we can restore the chunk on an undo
|
||||
vars.actions[chunk-1] = ""
|
||||
vars.actions_metadata[chunk-1]['Alternative Text'] = [{"Text": vars.actions_metadata[chunk-1]['Selected Text'], "Pinned": False, "Editted": True}] + vars.actions_metadata[chunk-1]['Alternative Text']
|
||||
vars.actions_metadata[chunk-1]['Selected Text'] = ''
|
||||
send_debug()
|
||||
else:
|
||||
if(k == 0):
|
||||
print(colors.GREEN + f"{lua_log_format_name(vars.lua_koboldbridge.logging_name)} edited prompt chunk" + colors.END)
|
||||
|
@ -1654,6 +1679,9 @@ def lua_set_chunk(k, v):
|
|||
vars._actions[chunk-1] = v
|
||||
vars.lua_edited.add(chunk)
|
||||
vars.actions[chunk-1] = v
|
||||
vars.actions_metadata[chunk-1]['Alternative Text'] = [{"Text": vars.actions_metadata[chunk-1]['Selected Text'], "Pinned": False, "Editted": True}] + vars.actions_metadata[chunk-1]['Alternative Text']
|
||||
vars.actions_metadata[chunk-1]['Selected Text'] = v
|
||||
send_debug()
|
||||
|
||||
#==================================================================#
|
||||
# Get model type as "gpt-2-xl", "gpt-neo-2.7B", etc.
|
||||
|
@ -1843,6 +1871,7 @@ def do_connect():
|
|||
#==================================================================#
|
||||
@socketio.on('message')
|
||||
def get_message(msg):
|
||||
if not vars.quiet:
|
||||
print("{0}Data received:{1}{2}".format(colors.GREEN, msg, colors.END))
|
||||
# Submit action
|
||||
if(msg['cmd'] == 'submit'):
|
||||
|
@ -1882,6 +1911,9 @@ def get_message(msg):
|
|||
# Back/Undo Action
|
||||
elif(msg['cmd'] == 'back'):
|
||||
actionback()
|
||||
# Forward/Redo Action
|
||||
elif(msg['cmd'] == 'redo'):
|
||||
actionredo()
|
||||
# EditMode Action (old)
|
||||
elif(msg['cmd'] == 'edit'):
|
||||
if(vars.mode == "play"):
|
||||
|
@ -2119,6 +2151,8 @@ def get_message(msg):
|
|||
vars.saveow = False
|
||||
elif(msg['cmd'] == 'seqsel'):
|
||||
selectsequence(msg['data'])
|
||||
elif(msg['cmd'] == 'seqpin'):
|
||||
pinsequence(msg['data'])
|
||||
elif(msg['cmd'] == 'setnumseq'):
|
||||
vars.numseqs = int(msg['data'])
|
||||
emit('from_server', {'cmd': 'setlabelnumseq', 'data': msg['data']})
|
||||
|
@ -2165,6 +2199,11 @@ def get_message(msg):
|
|||
refresh_settings()
|
||||
elif(not vars.remote and msg['cmd'] == 'importwi'):
|
||||
wiimportrequest()
|
||||
elif(msg['cmd'] == 'debug'):
|
||||
vars.debug = msg['data']
|
||||
emit('from_server', {'cmd': 'set_debug', 'data': msg['data']}, broadcast=True)
|
||||
if vars.debug:
|
||||
send_debug()
|
||||
|
||||
#==================================================================#
|
||||
# Send userscripts list to client
|
||||
|
@ -2493,7 +2532,25 @@ def actionsubmit(data, actionmode=0, force_submit=False, force_prompt_gen=False,
|
|||
vars.prompt = data
|
||||
else:
|
||||
vars.actions.append(data)
|
||||
# we now need to update the actions_metadata
|
||||
# we'll have two conditions.
|
||||
# 1. This is totally new (user entered)
|
||||
if len(vars.actions_metadata) < len(vars.actions):
|
||||
vars.actions_metadata.append({"Selected Text": data, "Alternative Text": []})
|
||||
else:
|
||||
# 2. We've selected a chunk of text that is was presented previously
|
||||
try:
|
||||
alternatives = [item['Text'] for item in vars.actions_metadata[len(vars.actions)-1]["Alternative Text"]]
|
||||
except:
|
||||
print(len(vars.actions))
|
||||
print(vars.actions_metadata)
|
||||
raise
|
||||
if data in alternatives:
|
||||
alternatives = [item for item in vars.actions_metadata[len(vars.actions)-1]["Alternative Text"] if item['Text'] != data]
|
||||
vars.actions_metadata[len(vars.actions)-1]["Alternative Text"] = alternatives
|
||||
vars.actions_metadata[len(vars.actions)-1]["Selected Text"] = data
|
||||
update_story_chunk('last')
|
||||
send_debug()
|
||||
|
||||
if(not vars.noai and vars.lua_koboldbridge.generating):
|
||||
# Off to the tokenizer!
|
||||
|
@ -2548,6 +2605,15 @@ def actionretry(data):
|
|||
# Remove last action if possible and resubmit
|
||||
if(vars.gamestarted if vars.useprompt else len(vars.actions) > 0):
|
||||
if(not vars.recentback and len(vars.actions) != 0 and len(vars.genseqs) == 0): # Don't pop if we're in the "Select sequence to keep" menu or if there are no non-prompt actions
|
||||
# We are going to move the selected text to alternative text in the actions_metadata variable so we can redo this action
|
||||
vars.actions_metadata[vars.actions]['Alternative Text'] = [{'Text': vars.actions_metadata[len(vars.actions)]['Selected Text'],
|
||||
'Pinned': False,
|
||||
"Previous Selection": True,
|
||||
"Edited": False}] + vars.actions_metadata[vars.actions]['Alternative Text']
|
||||
vars.actions_metadata[vars.actions]['Selected Text'] = ""
|
||||
|
||||
|
||||
|
||||
last_key = vars.actions.get_last_key()
|
||||
vars.actions.pop()
|
||||
remove_story_chunk(last_key + 1)
|
||||
|
@ -2555,6 +2621,7 @@ def actionretry(data):
|
|||
vars.recentedit = False
|
||||
vars.lua_koboldbridge.feedback = None
|
||||
actionsubmit("", actionmode=vars.actionmode, force_submit=True)
|
||||
send_debug()
|
||||
elif(not vars.useprompt):
|
||||
emit('from_server', {'cmd': 'errmsg', 'data': "Please enable \"Always Add Prompt\" to retry with your prompt."})
|
||||
|
||||
|
@ -2566,6 +2633,13 @@ def actionback():
|
|||
return
|
||||
# Remove last index of actions and refresh game screen
|
||||
if(len(vars.genseqs) == 0 and len(vars.actions) > 0):
|
||||
# We are going to move the selected text to alternative text in the actions_metadata variable so we can redo this action
|
||||
vars.actions_metadata[len(vars.actions)-1]['Alternative Text'] = [{'Text': vars.actions_metadata[len(vars.actions)-1]['Selected Text'],
|
||||
'Pinned': False,
|
||||
"Previous Selection": True,
|
||||
"Edited": False}] + vars.actions_metadata[len(vars.actions)-1]['Alternative Text']
|
||||
vars.actions_metadata[len(vars.actions)-1]['Selected Text'] = ""
|
||||
|
||||
last_key = vars.actions.get_last_key()
|
||||
vars.actions.pop()
|
||||
vars.recentback = True
|
||||
|
@ -2574,6 +2648,28 @@ def actionback():
|
|||
emit('from_server', {'cmd': 'errmsg', 'data': "Cannot delete the prompt."})
|
||||
else:
|
||||
vars.genseqs = []
|
||||
send_debug()
|
||||
|
||||
def actionredo():
|
||||
i = 0
|
||||
if len(vars.actions) < len(vars.actions_metadata):
|
||||
genout = [{"generated_text": item['Text']} for item in vars.actions_metadata[len(vars.actions)]['Alternative Text'] if (item["Previous Selection"]==True)]
|
||||
genout = genout + [{"generated_text": item['Text']} for item in vars.actions_metadata[len(vars.actions)]['Alternative Text'] if (item["Pinned"]==True) and (item["Previous Selection"]==False)]
|
||||
|
||||
if len(genout) == 1:
|
||||
vars.actions_metadata[len(vars.actions)]['Alternative Text'] = [item for item in vars.actions_metadata[len(vars.actions)]['Alternative Text'] if (item["Previous Selection"]!=True)]
|
||||
genresult(genout[0]['generated_text'], flash=True)
|
||||
else:
|
||||
# Store sequences in memory until selection is made
|
||||
vars.genseqs = genout
|
||||
|
||||
|
||||
# Send sequences to UI for selection
|
||||
genout = [[item['Text'], "redo"] for item in vars.actions_metadata[len(vars.actions)]['Alternative Text'] if (item["Previous Selection"]==True)]
|
||||
emit('from_server', {'cmd': 'genseqs', 'data': genout}, broadcast=True)
|
||||
else:
|
||||
emit('from_server', {'cmd': 'popuperror', 'data': "There's nothing to undo"}, broadcast=True)
|
||||
send_debug()
|
||||
|
||||
#==================================================================#
|
||||
#
|
||||
|
@ -2889,6 +2985,7 @@ def generate(txt, minimum, maximum, found_entries=None):
|
|||
found_entries = set()
|
||||
found_entries = tuple(found_entries.copy() for _ in range(vars.numseqs))
|
||||
|
||||
if not vars.quiet:
|
||||
print("{0}Min:{1}, Max:{2}, Txt:{3}{4}".format(colors.YELLOW, minimum, maximum, tokenizer.decode(txt), colors.END))
|
||||
|
||||
# Store context in memory to use it for comparison with generated content
|
||||
|
@ -2951,6 +3048,7 @@ def generate(txt, minimum, maximum, found_entries=None):
|
|||
# Deal with a single return sequence from generate()
|
||||
#==================================================================#
|
||||
def genresult(genout, flash=True):
|
||||
if not vars.quiet:
|
||||
print("{0}{1}{2}".format(colors.CYAN, genout, colors.END))
|
||||
|
||||
# Format output before continuing
|
||||
|
@ -2966,9 +3064,14 @@ def genresult(genout, flash=True):
|
|||
vars.prompt = genout
|
||||
else:
|
||||
vars.actions.append(genout)
|
||||
if len(vars.actions) > len(vars.actions_metadata):
|
||||
vars.actions_metadata.append({'Selected Text': genout, 'Alternative Text': []})
|
||||
else:
|
||||
vars.actions_metadata[len(vars.actions)-1]['Selected Text'] = genout
|
||||
update_story_chunk('last')
|
||||
if(flash):
|
||||
emit('from_server', {'cmd': 'texteffect', 'data': vars.actions.get_last_key() + 1 if len(vars.actions) else 0}, broadcast=True)
|
||||
send_debug()
|
||||
|
||||
#==================================================================#
|
||||
# Send generator sequences to the UI for selection
|
||||
|
@ -2978,14 +3081,34 @@ def genselect(genout):
|
|||
for result in genout:
|
||||
# Apply output formatting rules to sequences
|
||||
result["generated_text"] = applyoutputformatting(result["generated_text"])
|
||||
if not vars.quiet:
|
||||
print("{0}[Result {1}]\n{2}{3}".format(colors.CYAN, i, result["generated_text"], colors.END))
|
||||
i += 1
|
||||
|
||||
# Add the options to the actions metadata
|
||||
# If we've already generated text for this action but haven't selected one we'll want to kill all non-pinned, non-previous selection, and non-edited options then add the new ones
|
||||
if (len(vars.actions_metadata) > len(vars.actions)):
|
||||
if (vars.actions_metadata[len(vars.actions)]['Selected Text'] == ""):
|
||||
vars.actions_metadata[len(vars.actions)]['Alternative Text'] = [{"Text": item['Text'], "Pinned": item['Pinned'],
|
||||
"Previous Selection": item["Previous Selection"],
|
||||
"Edited": item["Edited"]} for item in vars.actions_metadata[len(vars.actions)]['Alternative Text']
|
||||
if item['Pinned'] or item["Previous Selection"] or item["Edited"]] + [{"Text": text["generated_text"],
|
||||
"Pinned": False, "Previous Selection": False, "Edited": False} for text in genout]
|
||||
else:
|
||||
vars.actions_metadata.append({'Selected Text': '', 'Alternative Text': [{"Text": text["generated_text"], "Pinned": False, "Previous Selection": False, "Edited": False} for text in genout]})
|
||||
else:
|
||||
vars.actions_metadata.append({'Selected Text': '', 'Alternative Text': [{"Text": text["generated_text"], "Pinned": False, "Previous Selection": False, "Edited": False} for text in genout]})
|
||||
|
||||
genout = [{"generated_text": item['Text']} for item in vars.actions_metadata[len(vars.actions)]['Alternative Text'] if (item["Previous Selection"]==False) and (item["Edited"]==False)]
|
||||
|
||||
# Store sequences in memory until selection is made
|
||||
vars.genseqs = genout
|
||||
|
||||
genout = [[item['Text'], "pinned" if item['Pinned'] else "normal"] for item in vars.actions_metadata[len(vars.actions)]['Alternative Text'] if (item["Previous Selection"]==False) and (item["Edited"]==False)]
|
||||
|
||||
# Send sequences to UI for selection
|
||||
emit('from_server', {'cmd': 'genseqs', 'data': genout}, broadcast=True)
|
||||
send_debug()
|
||||
|
||||
#==================================================================#
|
||||
# Send selected sequence to action log and refresh UI
|
||||
|
@ -2996,6 +3119,9 @@ def selectsequence(n):
|
|||
vars.lua_koboldbridge.feedback = vars.genseqs[int(n)]["generated_text"]
|
||||
if(len(vars.lua_koboldbridge.feedback) != 0):
|
||||
vars.actions.append(vars.lua_koboldbridge.feedback)
|
||||
#We'll want to remove the option from the alternative text and put it in selected text
|
||||
vars.actions_metadata[len(vars.actions)-1]['Alternative Text'] = [item for item in vars.actions_metadata[len(vars.actions)-1]['Alternative Text'] if item['Text'] != vars.lua_koboldbridge.feedback]
|
||||
vars.actions_metadata[len(vars.actions)-1]['Selected Text'] = vars.lua_koboldbridge.feedback
|
||||
update_story_chunk('last')
|
||||
emit('from_server', {'cmd': 'texteffect', 'data': vars.actions.get_last_key() + 1 if len(vars.actions) else 0}, broadcast=True)
|
||||
emit('from_server', {'cmd': 'hidegenseqs', 'data': ''}, broadcast=True)
|
||||
|
@ -3003,12 +3129,30 @@ def selectsequence(n):
|
|||
|
||||
if(vars.lua_koboldbridge.restart_sequence is not None):
|
||||
actionsubmit("", actionmode=vars.actionmode, force_submit=True, disable_recentrng=True)
|
||||
send_debug()
|
||||
|
||||
#==================================================================#
|
||||
# Pin/Unpin the selected sequence
|
||||
#==================================================================#
|
||||
def pinsequence(n):
|
||||
if n.isnumeric():
|
||||
text = vars.genseqs[int(n)]['generated_text']
|
||||
if text in [item['Text'] for item in vars.actions_metadata[len(vars.actions)]['Alternative Text']]:
|
||||
alternatives = vars.actions_metadata[len(vars.actions)]['Alternative Text']
|
||||
for i in range(len(alternatives)):
|
||||
if alternatives[i]['Text'] == text:
|
||||
alternatives[i]['Pinned'] = not alternatives[i]['Pinned']
|
||||
break
|
||||
vars.actions_metadata[len(vars.actions)]['Alternative Text'] = alternatives
|
||||
send_debug()
|
||||
|
||||
|
||||
#==================================================================#
|
||||
# Send transformers-style request to ngrok/colab host
|
||||
#==================================================================#
|
||||
def sendtocolab(txt, min, max):
|
||||
# Log request to console
|
||||
if not vars.quiet:
|
||||
print("{0}Tokens:{1}, Txt:{2}{3}".format(colors.YELLOW, min-1, txt, colors.END))
|
||||
|
||||
# Store context in memory to use it for comparison with generated content
|
||||
|
@ -3094,6 +3238,7 @@ def tpumtjgenerate(txt, minimum, maximum, found_entries=None):
|
|||
found_entries = set()
|
||||
found_entries = tuple(found_entries.copy() for _ in range(vars.numseqs))
|
||||
|
||||
if not vars.quiet:
|
||||
print("{0}Min:{1}, Max:{2}, Txt:{3}{4}".format(colors.YELLOW, minimum, maximum, tokenizer.decode(txt), colors.END))
|
||||
|
||||
vars._actions = vars.actions
|
||||
|
@ -3411,12 +3556,17 @@ def editsubmit(data):
|
|||
if(vars.editln == 0):
|
||||
vars.prompt = data
|
||||
else:
|
||||
vars.actions_metadata[vars.editln-1]['Alternative Text'] = vars.actions_metadata[vars.editln-1]['Alternative Text'] + [{"Text": vars.actions[vars.editln-1], "Pinned": False,
|
||||
"Previous Selection": False,
|
||||
"Edited": True}]
|
||||
vars.actions_metadata[vars.editln-1]['Selected Text'] = data
|
||||
vars.actions[vars.editln-1] = data
|
||||
|
||||
vars.mode = "play"
|
||||
update_story_chunk(vars.editln)
|
||||
emit('from_server', {'cmd': 'texteffect', 'data': vars.editln}, broadcast=True)
|
||||
emit('from_server', {'cmd': 'editmode', 'data': 'false'})
|
||||
send_debug()
|
||||
|
||||
#==================================================================#
|
||||
#
|
||||
|
@ -3428,10 +3578,14 @@ def deleterequest():
|
|||
# Send error message
|
||||
pass
|
||||
else:
|
||||
del vars.actions[vars.editln-1]
|
||||
vars.actions_metadata[vars.editln-1]['Alternative Text'] = [{"Text": vars.actions[vars.editln-1], "Pinned": False,
|
||||
"Previous Selection": True, "Edited": False}] + vars.actions_metadata[vars.editln-1]['Alternative Text']
|
||||
vars.actions_metadata[vars.editln-1]['Selected Text'] = ''
|
||||
vars.actions[vars.editln-1] = ''
|
||||
vars.mode = "play"
|
||||
remove_story_chunk(vars.editln)
|
||||
emit('from_server', {'cmd': 'editmode', 'data': 'false'})
|
||||
send_debug()
|
||||
|
||||
#==================================================================#
|
||||
#
|
||||
|
@ -3445,6 +3599,10 @@ def inlineedit(chunk, data):
|
|||
vars.prompt = data
|
||||
else:
|
||||
if(chunk-1 in vars.actions):
|
||||
vars.actions_metadata[chunk-1]['Alternative Text'] = vars.actions_metadata[chunk-1]['Alternative Text'] + [{"Text": vars.actions[chunk-1], "Pinned": False,
|
||||
"Previous Selection": False,
|
||||
"Edited": True}]
|
||||
vars.actions_metadata[chunk-1]['Selected Text'] = data
|
||||
vars.actions[chunk-1] = data
|
||||
else:
|
||||
print(f"WARNING: Attempted to edit non-existent chunk {chunk}")
|
||||
|
@ -3453,6 +3611,7 @@ def inlineedit(chunk, data):
|
|||
update_story_chunk(chunk)
|
||||
emit('from_server', {'cmd': 'texteffect', 'data': chunk}, broadcast=True)
|
||||
emit('from_server', {'cmd': 'editmode', 'data': 'false'}, broadcast=True)
|
||||
send_debug()
|
||||
|
||||
#==================================================================#
|
||||
#
|
||||
|
@ -3468,12 +3627,17 @@ def inlinedelete(chunk):
|
|||
emit('from_server', {'cmd': 'editmode', 'data': 'false'}, broadcast=True)
|
||||
else:
|
||||
if(chunk-1 in vars.actions):
|
||||
del vars.actions[chunk-1]
|
||||
vars.actions_metadata[chunk-1]['Alternative Text'] = [{"Text": vars.actions[chunk-1], "Pinned": False,
|
||||
"Previous Selection": True,
|
||||
"Edited": False}] + vars.actions_metadata[chunk-1]['Alternative Text']
|
||||
vars.actions_metadata[chunk-1]['Selected Text'] = ''
|
||||
vars.actions[chunk-1] = ''
|
||||
else:
|
||||
print(f"WARNING: Attempted to delete non-existent chunk {chunk}")
|
||||
setgamesaved(False)
|
||||
remove_story_chunk(chunk)
|
||||
emit('from_server', {'cmd': 'editmode', 'data': 'false'}, broadcast=True)
|
||||
send_debug()
|
||||
|
||||
#==================================================================#
|
||||
# Toggles the game mode for memory editing and sends UI commands
|
||||
|
@ -3822,6 +3986,7 @@ def anotesubmit(data, template=""):
|
|||
#==================================================================#
|
||||
def ikrequest(txt):
|
||||
# Log request to console
|
||||
if not vars.quiet:
|
||||
print("{0}Len:{1}, Txt:{2}{3}".format(colors.YELLOW, len(txt), txt, colors.END))
|
||||
|
||||
# Build request JSON data
|
||||
|
@ -3859,11 +4024,21 @@ def ikrequest(txt):
|
|||
genout = vars.lua_koboldbridge.outputs[1]
|
||||
assert genout is str
|
||||
|
||||
if not vars.quiet:
|
||||
print("{0}{1}{2}".format(colors.CYAN, genout, colors.END))
|
||||
vars.actions.append(genout)
|
||||
if len(vars.actions_metadata) < len(vars.actions):
|
||||
vars.actions_metadata.append({"Selected Text": genout, "Alternative Text": []})
|
||||
else:
|
||||
# 2. We've selected a chunk of text that is was presented previously
|
||||
alternatives = [item['Text'] for item in vars.actions_metadata[len(vars.actions)]["Alternative Text"]]
|
||||
if genout in alternatives:
|
||||
alternatives = [item for item in vars.actions_metadata[len(vars.actions)]["Alternative Text"] if item['Text'] != genout]
|
||||
vars.actions_metadata[len(vars.actions)]["Alternative Text"] = alternatives
|
||||
vars.actions_metadata[len(vars.actions)]["Selected Text"] = genout
|
||||
update_story_chunk('last')
|
||||
emit('from_server', {'cmd': 'texteffect', 'data': vars.actions.get_last_key() + 1 if len(vars.actions) else 0}, broadcast=True)
|
||||
|
||||
send_debug()
|
||||
set_aibusy(0)
|
||||
else:
|
||||
# Send error message to web client
|
||||
|
@ -3882,6 +4057,7 @@ def ikrequest(txt):
|
|||
#==================================================================#
|
||||
def oairequest(txt, min, max):
|
||||
# Log request to console
|
||||
if not vars.quiet:
|
||||
print("{0}Len:{1}, Txt:{2}{3}".format(colors.YELLOW, len(txt), txt, colors.END))
|
||||
|
||||
# Store context in memory to use it for comparison with generated content
|
||||
|
@ -3918,11 +4094,21 @@ def oairequest(txt, min, max):
|
|||
genout = vars.lua_koboldbridge.outputs[1]
|
||||
assert genout is str
|
||||
|
||||
if not vars.quiet:
|
||||
print("{0}{1}{2}".format(colors.CYAN, genout, colors.END))
|
||||
vars.actions.append(genout)
|
||||
if len(vars.actions_metadata) < len(vars.actions):
|
||||
vars.actions_metadata.append({"Selected Text": genout, "Alternative Text": []})
|
||||
else:
|
||||
# 2. We've selected a chunk of text that is was presented previously
|
||||
alternatives = [item['Text'] for item in vars.actions_metadata[len(vars.actions)]["Alternative Text"]]
|
||||
if genout in alternatives:
|
||||
alternatives = [item for item in vars.actions_metadata[len(vars.actions)]["Alternative Text"] if item['Text'] != genout]
|
||||
vars.actions_metadata[len(vars.actions)]["Alternative Text"] = alternatives
|
||||
vars.actions_metadata[len(vars.actions)]["Selected Text"] = genout
|
||||
update_story_chunk('last')
|
||||
emit('from_server', {'cmd': 'texteffect', 'data': vars.actions.get_last_key() + 1 if len(vars.actions) else 0}, broadcast=True)
|
||||
|
||||
send_debug()
|
||||
set_aibusy(0)
|
||||
else:
|
||||
# Send error message to web client
|
||||
|
@ -3950,12 +4136,15 @@ def exitModes():
|
|||
#==================================================================#
|
||||
# Launch in-browser save prompt
|
||||
#==================================================================#
|
||||
def saveas(name):
|
||||
def saveas(data):
|
||||
|
||||
name = data['name']
|
||||
savepins = data['pins']
|
||||
# Check if filename exists already
|
||||
name = utils.cleanfilename(name)
|
||||
if(not fileops.saveexists(name) or (vars.saveow and vars.svowname == name)):
|
||||
# All clear to save
|
||||
e = saveRequest(fileops.storypath(name))
|
||||
e = saveRequest(fileops.storypath(name), savepins=savepins)
|
||||
vars.saveow = False
|
||||
vars.svowname = ""
|
||||
if(e is None):
|
||||
|
@ -4031,7 +4220,7 @@ def savetofile():
|
|||
#==================================================================#
|
||||
# Save the story to specified path
|
||||
#==================================================================#
|
||||
def saveRequest(savpath):
|
||||
def saveRequest(savpath, savepins=True):
|
||||
if(savpath):
|
||||
# Leave Edit/Memory mode before continuing
|
||||
exitModes()
|
||||
|
@ -4047,6 +4236,8 @@ def saveRequest(savpath):
|
|||
js["authorsnote"] = vars.authornote
|
||||
js["anotetemplate"] = vars.authornotetemplate
|
||||
js["actions"] = tuple(vars.actions.values())
|
||||
if savepins:
|
||||
js["actions_metadata"] = vars.actions_metadata
|
||||
js["worldinfo"] = []
|
||||
js["wifolders_d"] = vars.wifolders_d
|
||||
js["wifolders_l"] = vars.wifolders_l
|
||||
|
@ -4172,6 +4363,12 @@ def loadRequest(loadpath, filename=None):
|
|||
vars.actions = structures.KoboldStoryRegister()
|
||||
actions = collections.deque(js["actions"])
|
||||
|
||||
if "actions_metadata" in js:
|
||||
vars.actions_metadata = js["actions_metadata"]
|
||||
else:
|
||||
vars.actions_metadata = [{'Selected Text': text, 'Alternative Text': []} for text in js["actions"]]
|
||||
|
||||
|
||||
if(len(vars.prompt.strip()) == 0):
|
||||
while(len(actions)):
|
||||
action = actions.popleft()
|
||||
|
@ -4588,6 +4785,7 @@ def wiimportrequest():
|
|||
if(vars.worldinfo[-1]["folder"] is not None):
|
||||
vars.wifolders_u[vars.worldinfo[-1]["folder"]].append(vars.worldinfo[-1])
|
||||
|
||||
if not vars.quiet:
|
||||
print("{0}".format(vars.worldinfo[0]))
|
||||
|
||||
# Refresh game screen
|
||||
|
@ -4606,6 +4804,7 @@ def newGameRequest():
|
|||
vars.prompt = ""
|
||||
vars.memory = ""
|
||||
vars.actions = structures.KoboldStoryRegister()
|
||||
vars.actions_metadata = []
|
||||
|
||||
vars.authornote = ""
|
||||
vars.authornotetemplate = vars.setauthornotetemplate
|
||||
|
@ -4689,6 +4888,13 @@ if(vars.model in ("TPUMeshTransformerGPTJ",)):
|
|||
},
|
||||
).start()
|
||||
|
||||
def send_debug():
|
||||
if vars.debug:
|
||||
debug_info = ""
|
||||
for variable in [["Action Length", len(vars.actions)], ["Actions Metadata Length", len(vars.actions_metadata)], ["Actions Metadata", vars.actions_metadata]]:
|
||||
debug_info = "{}{}: {}\n".format(debug_info, variable[0], variable[1])
|
||||
emit('from_server', {'cmd': 'debug_info', 'data': debug_info}, broadcast=True)
|
||||
|
||||
#==================================================================#
|
||||
# Final startup commands to launch Flask app
|
||||
#==================================================================#
|
||||
|
@ -4716,6 +4922,9 @@ if __name__ == "__main__":
|
|||
webbrowser.open_new('http://localhost:5000')
|
||||
print("{0}Server started!\nYou may now connect with a browser at http://127.0.0.1:5000/{1}".format(colors.GREEN, colors.END))
|
||||
vars.serverstarted = True
|
||||
if args.share:
|
||||
socketio.run(app, port=5000, host='0.0.0.0')
|
||||
else:
|
||||
socketio.run(app, port=5000)
|
||||
|
||||
else:
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
name: koboldai
|
||||
channels:
|
||||
- pytorch
|
||||
- conda-forge
|
||||
- defaults
|
||||
dependencies:
|
||||
- colorama
|
||||
- flask-socketio
|
||||
- pytorch
|
||||
- python=3.8.*
|
||||
- cudatoolkit=11.1
|
||||
- eventlet
|
||||
- markdown
|
||||
- bleach
|
||||
- pip
|
||||
- git
|
||||
- pip:
|
||||
- flask-cloudflared
|
||||
- flask-ngrok
|
||||
- lupa==1.10
|
|
@ -207,6 +207,17 @@ gensettingstf = [
|
|||
"step": 1,
|
||||
"default": 0,
|
||||
"tooltip": "Disables userscript generation modifiers."
|
||||
},
|
||||
{
|
||||
"uitype": "toggle",
|
||||
"unit": "bool",
|
||||
"label": "Debug",
|
||||
"id": "debug",
|
||||
"min": 0,
|
||||
"max": 1,
|
||||
"step": 1,
|
||||
"default": 0,
|
||||
"tooltip": "Show debug info"
|
||||
}
|
||||
]
|
||||
|
||||
|
@ -341,6 +352,17 @@ gensettingsik =[{
|
|||
"step": 1,
|
||||
"default": 0,
|
||||
"tooltip": "When enabled, the Memory text box in the Random Story dialog will be prefilled by default with your current story's memory instead of being empty."
|
||||
},
|
||||
{
|
||||
"uitype": "toggle",
|
||||
"unit": "bool",
|
||||
"label": "Debug",
|
||||
"id": "debug",
|
||||
"min": 0,
|
||||
"max": 1,
|
||||
"step": 1,
|
||||
"default": 0,
|
||||
"tooltip": "Show debug info"
|
||||
}
|
||||
]
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ var button_mode_label;
|
|||
var button_send;
|
||||
var button_actmem;
|
||||
var button_actback;
|
||||
var button_actfwd;
|
||||
var button_actretry;
|
||||
var button_actwi;
|
||||
var game_text;
|
||||
|
@ -38,6 +39,7 @@ var anote_menu;
|
|||
var anote_input;
|
||||
var anote_labelcur;
|
||||
var anote_slider;
|
||||
var debug_area;
|
||||
var popup;
|
||||
var popup_title;
|
||||
var popup_content;
|
||||
|
@ -49,6 +51,7 @@ var aidg_accept;
|
|||
var aidg_close;
|
||||
var saveaspopup;
|
||||
var saveasinput;
|
||||
var savepins;
|
||||
var topic;
|
||||
var saveas_accept;
|
||||
var saveas_close;
|
||||
|
@ -748,7 +751,7 @@ function enterMemoryMode() {
|
|||
setchatnamevisibility(false);
|
||||
showMessage("Edit the memory to be sent with each request to the AI.");
|
||||
button_actmem.html("Cancel");
|
||||
hide([button_actback, button_actretry, button_actwi]);
|
||||
hide([button_actback, button_actfwd, button_actretry, button_actwi]);
|
||||
// Display Author's Note field
|
||||
anote_menu.slideDown("fast");
|
||||
}
|
||||
|
@ -759,7 +762,7 @@ function exitMemoryMode() {
|
|||
setchatnamevisibility(chatmode);
|
||||
hideMessage();
|
||||
button_actmem.html("Memory");
|
||||
show([button_actback, button_actretry, button_actwi]);
|
||||
show([button_actback, button_actfwd, button_actretry, button_actwi]);
|
||||
input_text.val("");
|
||||
// Hide Author's Note field
|
||||
anote_menu.slideUp("fast");
|
||||
|
@ -768,7 +771,7 @@ function exitMemoryMode() {
|
|||
function enterWiMode() {
|
||||
showMessage("World Info will be added to memory only when the key appears in submitted text or the last action.");
|
||||
button_actwi.html("Accept");
|
||||
hide([button_actback, button_actmem, button_actretry, game_text]);
|
||||
hide([button_actback, button_actfwd, button_actmem, button_actretry, game_text]);
|
||||
setchatnamevisibility(false);
|
||||
show([wi_menu]);
|
||||
disableSendBtn();
|
||||
|
@ -780,7 +783,7 @@ function exitWiMode() {
|
|||
button_actwi.html("W Info");
|
||||
hide([wi_menu]);
|
||||
setchatnamevisibility(chatmode);
|
||||
show([button_actback, button_actmem, button_actretry, game_text]);
|
||||
show([button_actback, button_actfwd, button_actmem, button_actretry, game_text]);
|
||||
enableSendBtn();
|
||||
$("#gamescreen").removeClass("wigamescreen");
|
||||
}
|
||||
|
@ -884,7 +887,7 @@ function hideSaveAsPopup() {
|
|||
}
|
||||
|
||||
function sendSaveAsRequest() {
|
||||
socket.send({'cmd': 'saveasrequest', 'data': saveasinput.val()});
|
||||
socket.send({'cmd': 'saveasrequest', 'data': {"name": saveasinput.val(), "pins": savepins.val()}});
|
||||
}
|
||||
|
||||
function showLoadPopup() {
|
||||
|
@ -1142,9 +1145,9 @@ function updateSPStatItems(items) {
|
|||
function setStartState() {
|
||||
enableSendBtn();
|
||||
enableButtons([button_actmem, button_actwi]);
|
||||
disableButtons([button_actback, button_actretry]);
|
||||
disableButtons([button_actback, button_actfwd, button_actretry]);
|
||||
hide([wi_menu]);
|
||||
show([game_text, button_actmem, button_actwi, button_actback, button_actretry]);
|
||||
show([game_text, button_actmem, button_actwi, button_actback, button_actfwd, button_actretry]);
|
||||
hideMessage();
|
||||
hideWaitAnimation();
|
||||
button_actmem.html("Memory");
|
||||
|
@ -1160,10 +1163,41 @@ function parsegenseqs(seqs) {
|
|||
seqselcontents.html("");
|
||||
var i;
|
||||
for(i=0; i<seqs.length; i++) {
|
||||
seqselcontents.append("<div class=\"seqselitem\" id=\"seqsel"+i+"\" n=\""+i+"\">"+seqs[i].generated_text+"</div>");
|
||||
//setup selection data
|
||||
text_data = "<table><tr><td width=100%><div class=\"seqselitem\" id=\"seqsel"+i+"\" n=\""+i+"\">"+seqs[i][0]+"</div></td><td width=10>"
|
||||
|
||||
//Now do the icon (pin/redo)
|
||||
|
||||
if (seqs[i][1] == "redo") {
|
||||
text_data = text_data + "<span style=\"color: white\" class=\"oi oi-loop-circular\" title=\"Redo\" aria-hidden=\"true\" id=\"seqselpin"+i+"\" n=\""+i+"\"></span>"
|
||||
} else if (seqs[i][1] == "pinned") {
|
||||
text_data = text_data + "<span style=\"color: white\" class=\"oi oi-pin\" title=\"Pin\" aria-hidden=\"true\" id=\"seqselpin"+i+"\" n=\""+i+"\"></span>"
|
||||
} else {
|
||||
text_data = text_data + "<span style=\"color: grey\" class=\"oi oi-pin\" title=\"Pin\" aria-hidden=\"true\" id=\"seqselpin"+i+"\" n=\""+i+"\"></span>"
|
||||
}
|
||||
text_data = text_data + "</td></tr></table>"
|
||||
seqselcontents.append(text_data);
|
||||
|
||||
//setup on-click actions
|
||||
$("#seqsel"+i).on("click", function () {
|
||||
socket.send({'cmd': 'seqsel', 'data': $(this).attr("n")});
|
||||
});
|
||||
|
||||
//onclick for pin only
|
||||
if (seqs[i][1] != "redo") {
|
||||
$("#seqselpin"+i).on("click", function () {
|
||||
socket.send({'cmd': 'seqpin', 'data': $(this).attr("n")});
|
||||
if ($(this).attr("style") == "color: grey") {
|
||||
console.log($(this).attr("style"));
|
||||
$(this).css({"color": "white"});
|
||||
console.log($(this).attr("style"));
|
||||
} else {
|
||||
console.log($(this).attr("style"));
|
||||
$(this).css({"color": "grey"});
|
||||
console.log($(this).attr("style"));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
$('#seqselmenu').slideDown("slow");
|
||||
}
|
||||
|
@ -1741,6 +1775,7 @@ $(document).ready(function(){
|
|||
button_send = $('#btnsend');
|
||||
button_actmem = $('#btn_actmem');
|
||||
button_actback = $('#btn_actundo');
|
||||
button_actfwd = $('#btn_actredo');
|
||||
button_actretry = $('#btn_actretry');
|
||||
button_actwi = $('#btn_actwi');
|
||||
game_text = $('#gametext');
|
||||
|
@ -1750,6 +1785,7 @@ $(document).ready(function(){
|
|||
settings_menu = $("#settingsmenu");
|
||||
format_menu = $('#formatmenu');
|
||||
anote_menu = $('#anoterowcontainer');
|
||||
debug_area = $('#debugcontainer');
|
||||
wi_menu = $('#wimenu');
|
||||
anote_input = $('#anoteinput');
|
||||
anote_labelcur = $('#anotecur');
|
||||
|
@ -1765,6 +1801,7 @@ $(document).ready(function(){
|
|||
aidg_close = $("#btn_aidgpopupclose");
|
||||
saveaspopup = $("#saveascontainer");
|
||||
saveasinput = $("#savename");
|
||||
savepins = $("#savepins");
|
||||
topic = $("#topic");
|
||||
saveas_accept = $("#btn_saveasaccept");
|
||||
saveas_close = $("#btn_saveasclose");
|
||||
|
@ -1913,13 +1950,13 @@ $(document).ready(function(){
|
|||
// Enable or Disable buttons
|
||||
if(msg.data == "ready") {
|
||||
enableSendBtn();
|
||||
enableButtons([button_actmem, button_actwi, button_actback, button_actretry]);
|
||||
enableButtons([button_actmem, button_actwi, button_actback, button_actfwd, button_actretry]);
|
||||
hideWaitAnimation();
|
||||
gamestate = "ready";
|
||||
} else if(msg.data == "wait") {
|
||||
gamestate = "wait";
|
||||
disableSendBtn();
|
||||
disableButtons([button_actmem, button_actwi, button_actback, button_actretry]);
|
||||
disableButtons([button_actmem, button_actwi, button_actback, button_actfwd, button_actretry]);
|
||||
showWaitAnimation();
|
||||
} else if(msg.data == "start") {
|
||||
setStartState();
|
||||
|
@ -2251,6 +2288,14 @@ $(document).ready(function(){
|
|||
} else if(msg.cmd == "runs_remotely") {
|
||||
remote = true;
|
||||
hide([button_savetofile, button_import, button_importwi]);
|
||||
} else if(msg.cmd == "debug_info") {
|
||||
$("#debuginfo").val(msg.data);
|
||||
} else if(msg.cmd == "set_debug") {
|
||||
if(msg.data) {
|
||||
debug_area.removeClass("hidden");
|
||||
} else {
|
||||
debug_area.addClass("hidden");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -2330,6 +2375,12 @@ $(document).ready(function(){
|
|||
hidegenseqs();
|
||||
});
|
||||
|
||||
button_actfwd.on("click", function(ev) {
|
||||
hideMessage();
|
||||
//hidegenseqs();
|
||||
socket.send({'cmd': 'redo', 'data': ''});
|
||||
});
|
||||
|
||||
button_actmem.on("click", function(ev) {
|
||||
socket.send({'cmd': 'memory', 'data': ''});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="8" height="8" data-icon="pin" viewBox="0 0 8 8">
|
||||
<path d="M1.344 0a.502.502 0 0 0 .156 1h.5v2h-1c-.55 0-1 .45-1 1h3v3l.438 1 .563-1v-3h3c0-.55-.45-1-1-1h-1v-2h.5a.5.5 0 1 0 0-1h-4a.5.5 0 0 0-.094 0 .502.502 0 0 0-.063 0z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 295 B |
|
@ -123,6 +123,7 @@
|
|||
<button type="button" class="btn btn-primary" id="btn_actmem">Memory</button>
|
||||
<button type="button" class="btn btn-primary" id="btn_actwi">W Info</button>
|
||||
<button type="button" class="btn btn-primary" id="btn_actundo">Back</button>
|
||||
<button type="button" class="btn btn-primary" id="btn_actredo">Redo</button>
|
||||
<button type="button" class="btn btn-primary" id="btn_actretry">Retry</button>
|
||||
</div>
|
||||
<input type="text" id="chatname" class="form-control hidden" placeholder="Chat name">
|
||||
|
@ -185,6 +186,9 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hidden" id="debugcontainer">
|
||||
<textarea class="form-control" placeholder="Debug Info" id="debuginfo"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hidden" id="popupcontainer">
|
||||
|
@ -228,7 +232,9 @@
|
|||
<div class="popuptitletext">Enter Name For Save</div>
|
||||
</div>
|
||||
<div class="aidgpopupcontent">
|
||||
<input class="form-control" type="text" placeholder="Save Name" id="savename">
|
||||
<input class="form-control" type="text" placeholder="Save Name" id="savename"><br>
|
||||
<input type="checkbox" data-toggle="toggle" data-onstyle="success" id="savepins" checked>
|
||||
<div class="box-label">Save Pin Information</div>
|
||||
</div>
|
||||
<div class="popuperror hidden">
|
||||
<span></span>
|
||||
|
|
Loading…
Reference in New Issue