Merge pull request #15 from VE-FORBRYDERNE/story-manager
In-browser story management patch
This commit is contained in:
commit
aace0d058f
135
aiserver.py
135
aiserver.py
|
@ -26,7 +26,6 @@ import gensettings
|
||||||
from utils import debounce
|
from utils import debounce
|
||||||
import utils
|
import utils
|
||||||
import structures
|
import structures
|
||||||
import breakmodel
|
|
||||||
|
|
||||||
#==================================================================#
|
#==================================================================#
|
||||||
# Variables & Storage
|
# Variables & Storage
|
||||||
|
@ -111,6 +110,8 @@ class vars:
|
||||||
useprompt = True # Whether to send the full prompt with every submit action
|
useprompt = True # Whether to send the full prompt with every submit action
|
||||||
breakmodel = False # For GPU users, whether to use both system RAM and VRAM to conserve VRAM while offering speedup compared to CPU-only
|
breakmodel = False # For GPU users, whether to use both system RAM and VRAM to conserve VRAM while offering speedup compared to CPU-only
|
||||||
bmsupported = False # Whether the breakmodel option is supported (GPT-Neo/GPT-J only, currently)
|
bmsupported = False # Whether the breakmodel option is supported (GPT-Neo/GPT-J only, currently)
|
||||||
|
smandelete = False # Whether stories can be deleted from inside the browser
|
||||||
|
smanrename = False # Whether stories can be renamed from inside the browser
|
||||||
acregex_ai = re.compile(r'\n* *>(.|\n)*') # Pattern for matching adventure actions from the AI so we can remove them
|
acregex_ai = re.compile(r'\n* *>(.|\n)*') # Pattern for matching adventure actions from the AI so we can remove them
|
||||||
acregex_ui = re.compile(r'^ *(>.*)$', re.MULTILINE) # Pattern for matching actions in the HTML-escaped story so we can apply colouring, etc (make sure to encase part to format in parentheses)
|
acregex_ui = re.compile(r'^ *(>.*)$', re.MULTILINE) # Pattern for matching actions in the HTML-escaped story so we can apply colouring, etc (make sure to encase part to format in parentheses)
|
||||||
actionmode = 1
|
actionmode = 1
|
||||||
|
@ -173,12 +174,17 @@ parser.add_argument("--path", help="Specify the Path for local models (For model
|
||||||
parser.add_argument("--cpu", action='store_true', help="By default unattended launches are on the GPU use this option to force CPU usage.")
|
parser.add_argument("--cpu", action='store_true', help="By default unattended launches are on the GPU use this option to force CPU usage.")
|
||||||
parser.add_argument("--breakmodel", action='store_true', help="For models that support GPU-CPU hybrid generation, use this feature instead of GPU or CPU generation")
|
parser.add_argument("--breakmodel", action='store_true', help="For models that support GPU-CPU hybrid generation, use this feature instead of GPU or CPU generation")
|
||||||
parser.add_argument("--breakmodel_layers", type=int, help="Specify the number of layers to commit to system RAM if --breakmodel is used")
|
parser.add_argument("--breakmodel_layers", type=int, help="Specify the number of layers to commit to system RAM if --breakmodel is used")
|
||||||
|
parser.add_argument("--override_delete", action='store_true', help="Deleting stories from inside the browser is disabled if you are using --remote and enabled otherwise. Using this option will instead allow deleting stories if using --remote and prevent deleting stories otherwise.")
|
||||||
|
parser.add_argument("--override_rename", action='store_true', help="Renaming stories from inside the browser is disabled if you are using --remote and enabled otherwise. Using this option will instead allow renaming stories if using --remote and prevent renaming stories otherwise.")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
vars.model = args.model;
|
vars.model = args.model;
|
||||||
|
|
||||||
if args.remote:
|
if args.remote:
|
||||||
vars.remote = True;
|
vars.remote = True;
|
||||||
|
|
||||||
|
vars.smandelete = vars.remote == args.override_delete
|
||||||
|
vars.smanrename = vars.remote == args.override_rename
|
||||||
|
|
||||||
# Select a model to run
|
# Select a model to run
|
||||||
if args.model:
|
if args.model:
|
||||||
print("Welcome to KoboldAI!\nYou have selected the following Model:", vars.model)
|
print("Welcome to KoboldAI!\nYou have selected the following Model:", vars.model)
|
||||||
|
@ -363,7 +369,7 @@ log.setLevel(logging.ERROR)
|
||||||
|
|
||||||
# Start flask & SocketIO
|
# Start flask & SocketIO
|
||||||
print("{0}Initializing Flask... {1}".format(colors.PURPLE, colors.END), end="")
|
print("{0}Initializing Flask... {1}".format(colors.PURPLE, colors.END), end="")
|
||||||
from flask import Flask, render_template
|
from flask import Flask, render_template, Response, request
|
||||||
from flask_socketio import SocketIO, emit
|
from flask_socketio import SocketIO, emit
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
app.config['SECRET KEY'] = 'secret!'
|
app.config['SECRET KEY'] = 'secret!'
|
||||||
|
@ -385,6 +391,7 @@ if(not vars.model in ["InferKit", "Colab", "OAI", "ReadOnly"]):
|
||||||
if(vars.usegpu):
|
if(vars.usegpu):
|
||||||
generator = pipeline('text-generation', model=model, tokenizer=tokenizer, device=0)
|
generator = pipeline('text-generation', model=model, tokenizer=tokenizer, device=0)
|
||||||
elif(vars.breakmodel): # Use both RAM and VRAM (breakmodel)
|
elif(vars.breakmodel): # Use both RAM and VRAM (breakmodel)
|
||||||
|
import breakmodel
|
||||||
n_layers = model.config.num_layers
|
n_layers = model.config.num_layers
|
||||||
breakmodel.total_blocks = n_layers
|
breakmodel.total_blocks = n_layers
|
||||||
model.half().to('cpu')
|
model.half().to('cpu')
|
||||||
|
@ -435,6 +442,7 @@ if(not vars.model in ["InferKit", "Colab", "OAI", "ReadOnly"]):
|
||||||
if(vars.usegpu):
|
if(vars.usegpu):
|
||||||
generator = pipeline('text-generation', model=vars.model, device=0)
|
generator = pipeline('text-generation', model=vars.model, device=0)
|
||||||
elif(vars.breakmodel): # Use both RAM and VRAM (breakmodel)
|
elif(vars.breakmodel): # Use both RAM and VRAM (breakmodel)
|
||||||
|
import breakmodel
|
||||||
model = AutoModel.from_pretrained(vars.model)
|
model = AutoModel.from_pretrained(vars.model)
|
||||||
n_layers = model.config.num_layers
|
n_layers = model.config.num_layers
|
||||||
breakmodel.total_blocks = n_layers
|
breakmodel.total_blocks = n_layers
|
||||||
|
@ -494,9 +502,17 @@ def index():
|
||||||
return render_template('index.html')
|
return render_template('index.html')
|
||||||
@app.route('/download')
|
@app.route('/download')
|
||||||
def download():
|
def download():
|
||||||
# Leave Edit/Memory mode before continuing
|
save_format = request.args.get("format", "json").strip().lower()
|
||||||
exitModes()
|
|
||||||
|
if(save_format == "plaintext"):
|
||||||
|
txt = vars.prompt + "".join(vars.actions.values())
|
||||||
|
save = Response(txt)
|
||||||
|
filename = path.basename(vars.savedir)
|
||||||
|
if filename[-5:] == ".json":
|
||||||
|
filename = filename[:-5]
|
||||||
|
save.headers.set('Content-Disposition', 'attachment', filename='%s.txt' % filename)
|
||||||
|
return(save)
|
||||||
|
|
||||||
# Build json to write
|
# Build json to write
|
||||||
js = {}
|
js = {}
|
||||||
js["gamestarted"] = vars.gamestarted
|
js["gamestarted"] = vars.gamestarted
|
||||||
|
@ -516,8 +532,12 @@ def download():
|
||||||
"selective": wi["selective"],
|
"selective": wi["selective"],
|
||||||
"constant": wi["constant"]
|
"constant": wi["constant"]
|
||||||
})
|
})
|
||||||
save = flask.Response(json.dumps(js, indent=3))
|
|
||||||
save.headers.set('Content-Disposition', 'attachment', filename='%s.json' % path.basename(vars.savedir))
|
save = Response(json.dumps(js, indent=3))
|
||||||
|
filename = path.basename(vars.savedir)
|
||||||
|
if filename[-5:] == ".json":
|
||||||
|
filename = filename[:-5]
|
||||||
|
save.headers.set('Content-Disposition', 'attachment', filename='%s.json' % filename)
|
||||||
return(save)
|
return(save)
|
||||||
|
|
||||||
#============================ METHODS =============================#
|
#============================ METHODS =============================#
|
||||||
|
@ -528,7 +548,7 @@ def download():
|
||||||
@socketio.on('connect')
|
@socketio.on('connect')
|
||||||
def do_connect():
|
def do_connect():
|
||||||
print("{0}Client connected!{1}".format(colors.GREEN, colors.END))
|
print("{0}Client connected!{1}".format(colors.GREEN, colors.END))
|
||||||
emit('from_server', {'cmd': 'connected'})
|
emit('from_server', {'cmd': 'connected', 'smandelete': vars.smandelete, 'smanrename': vars.smanrename})
|
||||||
if(vars.remote):
|
if(vars.remote):
|
||||||
emit('from_server', {'cmd': 'runs_remotely'})
|
emit('from_server', {'cmd': 'runs_remotely'})
|
||||||
|
|
||||||
|
@ -597,11 +617,11 @@ def get_message(msg):
|
||||||
deleterequest()
|
deleterequest()
|
||||||
elif(msg['cmd'] == 'memory'):
|
elif(msg['cmd'] == 'memory'):
|
||||||
togglememorymode()
|
togglememorymode()
|
||||||
elif(msg['cmd'] == 'savetofile'):
|
elif(not vars.remote and msg['cmd'] == 'savetofile'):
|
||||||
savetofile()
|
savetofile()
|
||||||
elif(msg['cmd'] == 'loadfromfile'):
|
elif(not vars.remote and msg['cmd'] == 'loadfromfile'):
|
||||||
loadfromfile()
|
loadfromfile()
|
||||||
elif(msg['cmd'] == 'import'):
|
elif(not vars.remote and msg['cmd'] == 'import'):
|
||||||
importRequest()
|
importRequest()
|
||||||
elif(msg['cmd'] == 'newgame'):
|
elif(msg['cmd'] == 'newgame'):
|
||||||
newGameRequest()
|
newGameRequest()
|
||||||
|
@ -714,7 +734,11 @@ def get_message(msg):
|
||||||
elif(msg['cmd'] == 'loadselect'):
|
elif(msg['cmd'] == 'loadselect'):
|
||||||
vars.loadselect = msg["data"]
|
vars.loadselect = msg["data"]
|
||||||
elif(msg['cmd'] == 'loadrequest'):
|
elif(msg['cmd'] == 'loadrequest'):
|
||||||
loadRequest(getcwd()+"/stories/"+vars.loadselect+".json")
|
loadRequest(fileops.storypath(vars.loadselect))
|
||||||
|
elif(msg['cmd'] == 'deletestory'):
|
||||||
|
deletesave(msg['data'])
|
||||||
|
elif(msg['cmd'] == 'renamestory'):
|
||||||
|
renamesave(msg['data'], msg['newname'])
|
||||||
elif(msg['cmd'] == 'clearoverwrite'):
|
elif(msg['cmd'] == 'clearoverwrite'):
|
||||||
vars.svowname = ""
|
vars.svowname = ""
|
||||||
vars.saveow = False
|
vars.saveow = False
|
||||||
|
@ -738,7 +762,7 @@ def get_message(msg):
|
||||||
vars.adventure = msg['data']
|
vars.adventure = msg['data']
|
||||||
settingschanged()
|
settingschanged()
|
||||||
refresh_settings()
|
refresh_settings()
|
||||||
elif(msg['cmd'] == 'importwi'):
|
elif(not vars.remote and msg['cmd'] == 'importwi'):
|
||||||
wiimportrequest()
|
wiimportrequest()
|
||||||
|
|
||||||
#==================================================================#
|
#==================================================================#
|
||||||
|
@ -1848,16 +1872,62 @@ def saveas(name):
|
||||||
name = utils.cleanfilename(name)
|
name = utils.cleanfilename(name)
|
||||||
if(not fileops.saveexists(name) or (vars.saveow and vars.svowname == name)):
|
if(not fileops.saveexists(name) or (vars.saveow and vars.svowname == name)):
|
||||||
# All clear to save
|
# All clear to save
|
||||||
saveRequest(getcwd()+"/stories/"+name+".json")
|
e = saveRequest(fileops.storypath(name))
|
||||||
emit('from_server', {'cmd': 'hidesaveas', 'data': ''})
|
|
||||||
vars.saveow = False
|
vars.saveow = False
|
||||||
vars.svowname = ""
|
vars.svowname = ""
|
||||||
|
if(e is None):
|
||||||
|
emit('from_server', {'cmd': 'hidesaveas', 'data': ''})
|
||||||
|
else:
|
||||||
|
print("{0}{1}{2}".format(colors.RED, str(e), colors.END))
|
||||||
|
emit('from_server', {'cmd': 'popuperror', 'data': str(e)})
|
||||||
else:
|
else:
|
||||||
# File exists, prompt for overwrite
|
# File exists, prompt for overwrite
|
||||||
vars.saveow = True
|
vars.saveow = True
|
||||||
vars.svowname = name
|
vars.svowname = name
|
||||||
emit('from_server', {'cmd': 'askforoverwrite', 'data': ''})
|
emit('from_server', {'cmd': 'askforoverwrite', 'data': ''})
|
||||||
|
|
||||||
|
#==================================================================#
|
||||||
|
# Launch in-browser story-delete prompt
|
||||||
|
#==================================================================#
|
||||||
|
def deletesave(name):
|
||||||
|
name = utils.cleanfilename(name)
|
||||||
|
e = fileops.deletesave(name)
|
||||||
|
if(e is None):
|
||||||
|
if(vars.smandelete):
|
||||||
|
emit('from_server', {'cmd': 'hidepopupdelete', 'data': ''})
|
||||||
|
getloadlist()
|
||||||
|
else:
|
||||||
|
emit('from_server', {'cmd': 'popuperror', 'data': "The server denied your request to delete this story"})
|
||||||
|
else:
|
||||||
|
print("{0}{1}{2}".format(colors.RED, str(e), colors.END))
|
||||||
|
emit('from_server', {'cmd': 'popuperror', 'data': str(e)})
|
||||||
|
|
||||||
|
#==================================================================#
|
||||||
|
# Launch in-browser story-rename prompt
|
||||||
|
#==================================================================#
|
||||||
|
def renamesave(name, newname):
|
||||||
|
# Check if filename exists already
|
||||||
|
name = utils.cleanfilename(name)
|
||||||
|
newname = utils.cleanfilename(newname)
|
||||||
|
if(not fileops.saveexists(newname) or name == newname or (vars.saveow and vars.svowname == newname)):
|
||||||
|
e = fileops.renamesave(name, newname)
|
||||||
|
vars.saveow = False
|
||||||
|
vars.svowname = ""
|
||||||
|
if(e is None):
|
||||||
|
if(vars.smanrename):
|
||||||
|
emit('from_server', {'cmd': 'hidepopuprename', 'data': ''})
|
||||||
|
getloadlist()
|
||||||
|
else:
|
||||||
|
emit('from_server', {'cmd': 'popuperror', 'data': "The server denied your request to rename this story"})
|
||||||
|
else:
|
||||||
|
print("{0}{1}{2}".format(colors.RED, str(e), colors.END))
|
||||||
|
emit('from_server', {'cmd': 'popuperror', 'data': str(e)})
|
||||||
|
else:
|
||||||
|
# File exists, prompt for overwrite
|
||||||
|
vars.saveow = True
|
||||||
|
vars.svowname = newname
|
||||||
|
emit('from_server', {'cmd': 'askforoverwrite', 'data': ''})
|
||||||
|
|
||||||
#==================================================================#
|
#==================================================================#
|
||||||
# Save the currently running story
|
# Save the currently running story
|
||||||
#==================================================================#
|
#==================================================================#
|
||||||
|
@ -1906,33 +1976,30 @@ def saveRequest(savpath):
|
||||||
"constant": wi["constant"]
|
"constant": wi["constant"]
|
||||||
})
|
})
|
||||||
|
|
||||||
ln = len(vars.actions)
|
txt = vars.prompt + "".join(vars.actions.values())
|
||||||
|
|
||||||
if(ln > 0):
|
|
||||||
chunks = collections.deque()
|
|
||||||
i = 0
|
|
||||||
for key in reversed(vars.actions):
|
|
||||||
chunk = vars.actions[key]
|
|
||||||
chunks.appendleft(chunk)
|
|
||||||
i += 1
|
|
||||||
|
|
||||||
if(ln > 0):
|
|
||||||
txt = vars.prompt + "".join(chunks)
|
|
||||||
elif(ln == 0):
|
|
||||||
txt = vars.prompt
|
|
||||||
|
|
||||||
# Write it
|
# Write it
|
||||||
file = open(savpath, "w")
|
try:
|
||||||
|
file = open(savpath, "w")
|
||||||
|
except Exception as e:
|
||||||
|
return e
|
||||||
try:
|
try:
|
||||||
file.write(json.dumps(js, indent=3))
|
file.write(json.dumps(js, indent=3))
|
||||||
finally:
|
except Exception as e:
|
||||||
file.close()
|
file.close()
|
||||||
|
return e
|
||||||
file = open(txtpath, "w")
|
file.close()
|
||||||
|
|
||||||
|
try:
|
||||||
|
file = open(txtpath, "w")
|
||||||
|
except Exception as e:
|
||||||
|
return e
|
||||||
try:
|
try:
|
||||||
file.write(txt)
|
file.write(txt)
|
||||||
finally:
|
except Exception as e:
|
||||||
file.close()
|
file.close()
|
||||||
|
return e
|
||||||
|
file.close()
|
||||||
|
|
||||||
print("{0}Story saved to {1}!{2}".format(colors.GREEN, path.basename(savpath), colors.END))
|
print("{0}Story saved to {1}!{2}".format(colors.GREEN, path.basename(savpath), colors.END))
|
||||||
|
|
||||||
|
|
27
fileops.py
27
fileops.py
|
@ -1,6 +1,7 @@
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
from tkinter import filedialog
|
from tkinter import filedialog
|
||||||
from os import getcwd, listdir, path
|
from os import getcwd, listdir, path
|
||||||
|
import os
|
||||||
import json
|
import json
|
||||||
|
|
||||||
#==================================================================#
|
#==================================================================#
|
||||||
|
@ -54,6 +55,12 @@ def getdirpath(dir, title):
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
#==================================================================#
|
||||||
|
# Returns the path (as a string) to the given story by its name
|
||||||
|
#==================================================================#
|
||||||
|
def storypath(name):
|
||||||
|
return path.join(path.dirname(path.realpath(__file__)), "stories", name + ".json")
|
||||||
|
|
||||||
#==================================================================#
|
#==================================================================#
|
||||||
# Returns an array of dicts containing story files in /stories
|
# Returns an array of dicts containing story files in /stories
|
||||||
#==================================================================#
|
#==================================================================#
|
||||||
|
@ -83,4 +90,22 @@ def getstoryfiles():
|
||||||
# Returns True if json file exists with requested save name
|
# Returns True if json file exists with requested save name
|
||||||
#==================================================================#
|
#==================================================================#
|
||||||
def saveexists(name):
|
def saveexists(name):
|
||||||
return path.exists(path.dirname(path.realpath(__file__))+"/stories/"+name+".json")
|
return path.exists(storypath(name))
|
||||||
|
|
||||||
|
#==================================================================#
|
||||||
|
# Delete save file by name; returns None if successful, or the exception if not
|
||||||
|
#==================================================================#
|
||||||
|
def deletesave(name):
|
||||||
|
try:
|
||||||
|
os.remove(storypath(name))
|
||||||
|
except Exception as e:
|
||||||
|
return e
|
||||||
|
|
||||||
|
#==================================================================#
|
||||||
|
# Rename save file; returns None if successful, or the exception if not
|
||||||
|
#==================================================================#
|
||||||
|
def renamesave(name, new_name):
|
||||||
|
try:
|
||||||
|
os.replace(storypath(name), storypath(new_name))
|
||||||
|
except Exception as e:
|
||||||
|
return e
|
||||||
|
|
|
@ -49,7 +49,6 @@ var saveasinput;
|
||||||
var topic;
|
var topic;
|
||||||
var saveas_accept;
|
var saveas_accept;
|
||||||
var saveas_close;
|
var saveas_close;
|
||||||
var saveasoverwrite;
|
|
||||||
var loadpopup;
|
var loadpopup;
|
||||||
var loadcontent;
|
var loadcontent;
|
||||||
var load_accept;
|
var load_accept;
|
||||||
|
@ -70,6 +69,8 @@ var connected = false;
|
||||||
var newly_loaded = true;
|
var newly_loaded = true;
|
||||||
var current_editing_chunk = null;
|
var current_editing_chunk = null;
|
||||||
var chunk_conflict = false;
|
var chunk_conflict = false;
|
||||||
|
var sman_allow_delete = false;
|
||||||
|
var sman_allow_rename = false;
|
||||||
|
|
||||||
// Key states
|
// Key states
|
||||||
var shift_down = false;
|
var shift_down = false;
|
||||||
|
@ -286,7 +287,7 @@ function addWiLine(ob) {
|
||||||
disableWiSelective(ob.num);
|
disableWiSelective(ob.num);
|
||||||
});
|
});
|
||||||
$("#constant-key-"+ob.num).on("click", function () {
|
$("#constant-key-"+ob.num).on("click", function () {
|
||||||
element = $("#constant-key-"+ob.num);
|
var element = $("#constant-key-"+ob.num);
|
||||||
if(element.hasClass("constant-key-icon-enabled")) {
|
if(element.hasClass("constant-key-icon-enabled")) {
|
||||||
socket.send({'cmd': 'wiconstantoff', 'data': ob.num});
|
socket.send({'cmd': 'wiconstantoff', 'data': ob.num});
|
||||||
element.removeClass("constant-key-icon-enabled")
|
element.removeClass("constant-key-icon-enabled")
|
||||||
|
@ -542,7 +543,7 @@ function hideSaveAsPopup() {
|
||||||
saveaspopup.removeClass("flex");
|
saveaspopup.removeClass("flex");
|
||||||
saveaspopup.addClass("hidden");
|
saveaspopup.addClass("hidden");
|
||||||
saveasinput.val("");
|
saveasinput.val("");
|
||||||
hide([saveasoverwrite]);
|
hide([$(".saveasoverwrite"), $(".popuperror")]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendSaveAsRequest() {
|
function sendSaveAsRequest() {
|
||||||
|
@ -566,20 +567,67 @@ function buildLoadList(ar) {
|
||||||
showLoadPopup();
|
showLoadPopup();
|
||||||
var i;
|
var i;
|
||||||
for(i=0; i<ar.length; i++) {
|
for(i=0; i<ar.length; i++) {
|
||||||
loadcontent.append("<div class=\"loadlistitem\" id=\"load"+i+"\" name=\""+ar[i].name+"\">\
|
loadcontent.append("<div class=\"flex\">\
|
||||||
<div>"+ar[i].name+"</div>\
|
<div class=\"loadlistpadding\"></div>\
|
||||||
<div>"+ar[i].actions+"</div>\
|
<span class=\"loadlisticon loadlisticon-delete oi oi-x "+(sman_allow_delete ? "allowed" : "")+"\" id=\"loaddelete"+i+"\" "+(sman_allow_delete ? "title=\"Delete story\"" : "")+" aria-hidden=\"true\"></span>\
|
||||||
|
<div class=\"loadlistpadding\"></div>\
|
||||||
|
<span class=\"loadlisticon loadlisticon-rename oi oi-pencil "+(sman_allow_rename ? "allowed" : "")+"\" id=\"loadrename"+i+"\" "+(sman_allow_rename ? "title=\"Rename story\"" : "")+" aria-hidden=\"true\"></span>\
|
||||||
|
<div class=\"loadlistpadding\"></div>\
|
||||||
|
<div class=\"loadlistitem\" id=\"load"+i+"\" name=\""+ar[i].name+"\">\
|
||||||
|
<div>"+ar[i].name+"</div>\
|
||||||
|
<div class=\"flex-push-right\">"+ar[i].actions+"</div>\
|
||||||
|
</div>\
|
||||||
</div>");
|
</div>");
|
||||||
$("#load"+i).on("click", function () {
|
$("#load"+i).on("click", function () {
|
||||||
enableButtons([load_accept]);
|
enableButtons([load_accept]);
|
||||||
socket.send({'cmd': 'loadselect', 'data': $(this).attr("name")});
|
socket.send({'cmd': 'loadselect', 'data': $(this).attr("name")});
|
||||||
highlightLoadLine($(this));
|
highlightLoadLine($(this));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$("#loaddelete"+i).off("click").on("click", (function (name) {
|
||||||
|
return function () {
|
||||||
|
if(!sman_allow_delete) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$("#loadcontainerdelete-storyname").text(name);
|
||||||
|
$("#btn_dsaccept").off("click").on("click", (function (name) {
|
||||||
|
return function () {
|
||||||
|
hide([$(".saveasoverwrite"), $(".popuperror")]);
|
||||||
|
socket.send({'cmd': 'deletestory', 'data': name});
|
||||||
|
}
|
||||||
|
})(name));
|
||||||
|
$("#loadcontainerdelete").removeClass("hidden").addClass("flex");
|
||||||
|
}
|
||||||
|
})(ar[i].name));
|
||||||
|
|
||||||
|
$("#loadrename"+i).off("click").on("click", (function (name) {
|
||||||
|
return function () {
|
||||||
|
if(!sman_allow_rename) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$("#newsavename").val("")
|
||||||
|
$("#loadcontainerrename-storyname").text(name);
|
||||||
|
var submit = (function (name) {
|
||||||
|
return function () {
|
||||||
|
hide([$(".saveasoverwrite"), $(".popuperror")]);
|
||||||
|
socket.send({'cmd': 'renamestory', 'data': name, 'newname': $("#newsavename").val()});
|
||||||
|
}
|
||||||
|
})(name);
|
||||||
|
$("#btn_rensaccept").off("click").on("click", submit);
|
||||||
|
$("#newsavename").off("keydown").on("keydown", function (ev) {
|
||||||
|
if (ev.which == 13 && $(this).val() != "") {
|
||||||
|
submit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$("#loadcontainerrename").removeClass("hidden").addClass("flex");
|
||||||
|
$("#newsavename").val(name).select();
|
||||||
|
}
|
||||||
|
})(ar[i].name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function highlightLoadLine(ref) {
|
function highlightLoadLine(ref) {
|
||||||
$("#loadlistcontent > div").removeClass("popuplistselected");
|
$("#loadlistcontent > div > div.popuplistselected").removeClass("popuplistselected");
|
||||||
ref.addClass("popuplistselected");
|
ref.addClass("popuplistselected");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -687,26 +735,26 @@ function chunkOnKeyDown(event) {
|
||||||
switch(event.keyCode) {
|
switch(event.keyCode) {
|
||||||
case 37: // left
|
case 37: // left
|
||||||
case 39: // right
|
case 39: // right
|
||||||
old_range = getSelection().getRangeAt(0);
|
var old_range = getSelection().getRangeAt(0);
|
||||||
old_range_start = old_range.startOffset;
|
var old_range_start = old_range.startOffset;
|
||||||
old_range_end = old_range.endOffset;
|
var old_range_end = old_range.endOffset;
|
||||||
old_range_ancestor = old_range.commonAncestorContainer;
|
var old_range_ancestor = old_range.commonAncestorContainer;
|
||||||
old_range_start_container = old_range.startContainer;
|
var old_range_start_container = old_range.startContainer;
|
||||||
old_range_end_container = old_range.endContainer;
|
var old_range_end_container = old_range.endContainer;
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
// Wait a few milliseconds and check if the caret has moved
|
// Wait a few milliseconds and check if the caret has moved
|
||||||
new_selection = getSelection();
|
var new_selection = getSelection();
|
||||||
new_range = new_selection.getRangeAt(0);
|
var new_range = new_selection.getRangeAt(0);
|
||||||
if(old_range_start != new_range.startOffset || old_range_end != new_range.endOffset || old_range_ancestor != new_range.commonAncestorContainer || old_range_start_container != new_range.startContainer || old_range_end_container != new_range.endContainer) {
|
if(old_range_start != new_range.startOffset || old_range_end != new_range.endOffset || old_range_ancestor != new_range.commonAncestorContainer || old_range_start_container != new_range.startContainer || old_range_end_container != new_range.endContainer) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// If it hasn't moved, we're at the beginning or end of a chunk
|
// If it hasn't moved, we're at the beginning or end of a chunk
|
||||||
// and the caret must be moved to a different chunk
|
// and the caret must be moved to a different chunk
|
||||||
chunk = document.activeElement;
|
var chunk = document.activeElement;
|
||||||
switch(event.keyCode) {
|
switch(event.keyCode) {
|
||||||
case 37: // left
|
case 37: // left
|
||||||
if((chunk = chunk.previousSibling) && chunk.tagName == "CHUNK") {
|
if((chunk = chunk.previousSibling) && chunk.tagName == "CHUNK") {
|
||||||
range = document.createRange();
|
var range = document.createRange();
|
||||||
range.selectNodeContents(chunk);
|
range.selectNodeContents(chunk);
|
||||||
range.collapse(false);
|
range.collapse(false);
|
||||||
new_selection.removeAllRanges();
|
new_selection.removeAllRanges();
|
||||||
|
@ -723,7 +771,7 @@ function chunkOnKeyDown(event) {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case 8: // backspace
|
case 8: // backspace
|
||||||
old_length = document.activeElement.innerText.length;
|
var old_length = document.activeElement.innerText.length;
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
// Wait a few milliseconds and compare the chunk's length
|
// Wait a few milliseconds and compare the chunk's length
|
||||||
if(old_length != document.activeElement.innerText.length) {
|
if(old_length != document.activeElement.innerText.length) {
|
||||||
|
@ -731,8 +779,8 @@ function chunkOnKeyDown(event) {
|
||||||
}
|
}
|
||||||
// If it's the same, we're at the beginning of a chunk
|
// If it's the same, we're at the beginning of a chunk
|
||||||
if((chunk = document.activeElement.previousSibling) && chunk.tagName == "CHUNK") {
|
if((chunk = document.activeElement.previousSibling) && chunk.tagName == "CHUNK") {
|
||||||
range = document.createRange();
|
var range = document.createRange();
|
||||||
selection = getSelection();
|
var selection = getSelection();
|
||||||
range.selectNodeContents(chunk);
|
range.selectNodeContents(chunk);
|
||||||
range.collapse(false);
|
range.collapse(false);
|
||||||
selection.removeAllRanges();
|
selection.removeAllRanges();
|
||||||
|
@ -771,7 +819,7 @@ function submitEditedChunk(event) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
chunk = current_editing_chunk;
|
var chunk = current_editing_chunk;
|
||||||
current_editing_chunk = null;
|
current_editing_chunk = null;
|
||||||
|
|
||||||
// Submit the edited chunk if it's not empty, otherwise delete it
|
// Submit the edited chunk if it's not empty, otherwise delete it
|
||||||
|
@ -795,6 +843,8 @@ $(document).ready(function(){
|
||||||
button_save = $('#btn_save');
|
button_save = $('#btn_save');
|
||||||
button_saveas = $('#btn_saveas');
|
button_saveas = $('#btn_saveas');
|
||||||
button_savetofile = $('#btn_savetofile');
|
button_savetofile = $('#btn_savetofile');
|
||||||
|
button_download = $('#btn_download');
|
||||||
|
button_downloadtxt= $('#btn_downloadtxt');
|
||||||
button_load = $('#btn_load');
|
button_load = $('#btn_load');
|
||||||
button_loadfrfile = $('#btn_loadfromfile');
|
button_loadfrfile = $('#btn_loadfromfile');
|
||||||
button_import = $("#btn_import");
|
button_import = $("#btn_import");
|
||||||
|
@ -833,7 +883,6 @@ $(document).ready(function(){
|
||||||
topic = $("#topic");
|
topic = $("#topic");
|
||||||
saveas_accept = $("#btn_saveasaccept");
|
saveas_accept = $("#btn_saveasaccept");
|
||||||
saveas_close = $("#btn_saveasclose");
|
saveas_close = $("#btn_saveasclose");
|
||||||
saveasoverwrite = $("#saveasoverwrite");
|
|
||||||
loadpopup = $("#loadcontainer");
|
loadpopup = $("#loadcontainer");
|
||||||
loadcontent = $("#loadlistcontent");
|
loadcontent = $("#loadlistcontent");
|
||||||
load_accept = $("#btn_loadaccept");
|
load_accept = $("#btn_loadaccept");
|
||||||
|
@ -853,6 +902,8 @@ $(document).ready(function(){
|
||||||
socket.on('from_server', function(msg) {
|
socket.on('from_server', function(msg) {
|
||||||
if(msg.cmd == "connected") {
|
if(msg.cmd == "connected") {
|
||||||
// Connected to Server Actions
|
// Connected to Server Actions
|
||||||
|
sman_allow_delete = msg.hasOwnProperty("smandelete") && msg.smandelete;
|
||||||
|
sman_allow_rename = msg.hasOwnProperty("smanrename") && msg.smanrename;
|
||||||
connected = true;
|
connected = true;
|
||||||
connect_status.html("<b>Connected to KoboldAI Process!</b>");
|
connect_status.html("<b>Connected to KoboldAI Process!</b>");
|
||||||
connect_status.removeClass("color_orange");
|
connect_status.removeClass("color_orange");
|
||||||
|
@ -870,7 +921,7 @@ $(document).ready(function(){
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else if(msg.cmd == "updatescreen") {
|
} else if(msg.cmd == "updatescreen") {
|
||||||
_gamestarted = gamestarted;
|
var _gamestarted = gamestarted;
|
||||||
gamestarted = msg.gamestarted;
|
gamestarted = msg.gamestarted;
|
||||||
if(_gamestarted != gamestarted) {
|
if(_gamestarted != gamestarted) {
|
||||||
action_mode = 0;
|
action_mode = 0;
|
||||||
|
@ -1048,6 +1099,14 @@ $(document).ready(function(){
|
||||||
} else if(msg.cmd == "popupshow") {
|
} else if(msg.cmd == "popupshow") {
|
||||||
// Show/Hide Popup
|
// Show/Hide Popup
|
||||||
popupShow(msg.data);
|
popupShow(msg.data);
|
||||||
|
} else if(msg.cmd == "hidepopupdelete") {
|
||||||
|
// Hide the dialog box that asks you to confirm deletion of a story
|
||||||
|
$("#loadcontainerdelete").removeClass("flex").addClass("hidden");
|
||||||
|
hide([$(".saveasoverwrite"), $(".popuperror")]);
|
||||||
|
} else if(msg.cmd == "hidepopuprename") {
|
||||||
|
// Hide the story renaming dialog box
|
||||||
|
$("#loadcontainerrename").removeClass("flex").addClass("hidden");
|
||||||
|
hide([$(".saveasoverwrite"), $(".popuperror")]);
|
||||||
} else if(msg.cmd == "addimportline") {
|
} else if(msg.cmd == "addimportline") {
|
||||||
// Add import popup entry
|
// Add import popup entry
|
||||||
addImportLine(msg.data);
|
addImportLine(msg.data);
|
||||||
|
@ -1081,7 +1140,11 @@ $(document).ready(function(){
|
||||||
buildLoadList(msg.data);
|
buildLoadList(msg.data);
|
||||||
} else if(msg.cmd == "askforoverwrite") {
|
} else if(msg.cmd == "askforoverwrite") {
|
||||||
// Show overwrite warning
|
// Show overwrite warning
|
||||||
show([saveasoverwrite]);
|
show([$(".saveasoverwrite")]);
|
||||||
|
} else if(msg.cmd == "popuperror") {
|
||||||
|
// Show error in the current dialog box
|
||||||
|
$(".popuperror").text(msg.data);
|
||||||
|
show([$(".popuperror")]);
|
||||||
} else if(msg.cmd == "genseqs") {
|
} else if(msg.cmd == "genseqs") {
|
||||||
// Parse generator sequences to UI
|
// Parse generator sequences to UI
|
||||||
parsegenseqs(msg.data);
|
parsegenseqs(msg.data);
|
||||||
|
@ -1211,6 +1274,14 @@ $(document).ready(function(){
|
||||||
saveas_accept.on("click", function(ev) {
|
saveas_accept.on("click", function(ev) {
|
||||||
sendSaveAsRequest();
|
sendSaveAsRequest();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
button_download.on("click", function(ev) {
|
||||||
|
window.open("/download", "_blank");
|
||||||
|
});
|
||||||
|
|
||||||
|
button_downloadtxt.on("click", function(ev) {
|
||||||
|
window.open("/download?format=plaintext", "_blank");
|
||||||
|
});
|
||||||
|
|
||||||
button_load.on("click", function(ev) {
|
button_load.on("click", function(ev) {
|
||||||
socket.send({'cmd': 'loadlistrequest', 'data': ''});
|
socket.send({'cmd': 'loadlistrequest', 'data': ''});
|
||||||
|
@ -1238,6 +1309,25 @@ $(document).ready(function(){
|
||||||
ns_close.on("click", function(ev) {
|
ns_close.on("click", function(ev) {
|
||||||
hideNewStoryPopup();
|
hideNewStoryPopup();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$("#btn_dsclose").on("click", function () {
|
||||||
|
$("#loadcontainerdelete").removeClass("flex").addClass("hidden");
|
||||||
|
hide([$(".saveasoverwrite"), $(".popuperror")]);
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#newsavename").on("input", function (ev) {
|
||||||
|
if($(this).val() == "") {
|
||||||
|
disableButtons([$("#btn_rensaccept")]);
|
||||||
|
} else {
|
||||||
|
enableButtons([$("#btn_rensaccept")]);
|
||||||
|
}
|
||||||
|
hide([$(".saveasoverwrite"), $(".popuperror")]);
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#btn_rensclose").on("click", function () {
|
||||||
|
$("#loadcontainerrename").removeClass("flex").addClass("hidden");
|
||||||
|
hide([$(".saveasoverwrite"), $(".popuperror")]);
|
||||||
|
});
|
||||||
|
|
||||||
button_rndgame.on("click", function(ev) {
|
button_rndgame.on("click", function(ev) {
|
||||||
showRandomStoryPopup();
|
showRandomStoryPopup();
|
||||||
|
@ -1262,7 +1352,7 @@ $(document).ready(function(){
|
||||||
} else {
|
} else {
|
||||||
enableButtons([saveas_accept]);
|
enableButtons([saveas_accept]);
|
||||||
}
|
}
|
||||||
hide([saveasoverwrite]);
|
hide([$(".saveasoverwrite"), $(".popuperror")]);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Bind Enter button to submit
|
// Bind Enter button to submit
|
||||||
|
|
|
@ -271,12 +271,6 @@ chunk, chunk * {
|
||||||
margin-top: 200px;
|
margin-top: 200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#saveasoverwrite {
|
|
||||||
color: #ff9900;
|
|
||||||
font-weight: bold;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
#loadpopup {
|
#loadpopup {
|
||||||
width: 500px;
|
width: 500px;
|
||||||
background-color: #262626;
|
background-color: #262626;
|
||||||
|
@ -291,6 +285,18 @@ chunk, chunk * {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#loadpopupdelete {
|
||||||
|
width: 350px;
|
||||||
|
background-color: #262626;
|
||||||
|
margin-top: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#loadpopuprename {
|
||||||
|
width: 350px;
|
||||||
|
background-color: #262626;
|
||||||
|
margin-top: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
#loadlistcontent {
|
#loadlistcontent {
|
||||||
height: 325px;
|
height: 325px;
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
|
@ -319,6 +325,12 @@ chunk, chunk * {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dialogheader {
|
||||||
|
padding: 10px 40px 10px 40px;
|
||||||
|
color: #737373;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
.anotelabel {
|
.anotelabel {
|
||||||
font-size: 10pt;
|
font-size: 10pt;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
|
@ -556,16 +568,16 @@ chunk, chunk * {
|
||||||
}
|
}
|
||||||
|
|
||||||
.loadlistheader {
|
.loadlistheader {
|
||||||
padding-left: 10px;
|
padding-left: 68px;
|
||||||
display: grid;
|
padding-right: 20px;
|
||||||
grid-template-columns: 80% 20%;
|
display: flex;
|
||||||
color: #737373;
|
color: #737373;
|
||||||
}
|
}
|
||||||
|
|
||||||
.loadlistitem {
|
.loadlistitem {
|
||||||
padding: 5px 10px 5px 10px;
|
padding: 5px 10px 5px 10px;
|
||||||
display: grid;
|
display: flex;
|
||||||
grid-template-columns: 80% 20%;
|
flex-grow: 1;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
|
|
||||||
-moz-transition: background-color 0.25s ease-in;
|
-moz-transition: background-color 0.25s ease-in;
|
||||||
|
@ -579,6 +591,30 @@ chunk, chunk * {
|
||||||
background-color: #688f1f;
|
background-color: #688f1f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.loadlistpadding {
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loadlisticon {
|
||||||
|
color: #333
|
||||||
|
}
|
||||||
|
|
||||||
|
.loadlisticon.allowed {
|
||||||
|
color: #ddd
|
||||||
|
}
|
||||||
|
|
||||||
|
.loadlisticon.allowed:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loadlisticon-delete.allowed:hover {
|
||||||
|
color: #ef2929
|
||||||
|
}
|
||||||
|
|
||||||
|
.loadlisticon-rename.allowed:hover {
|
||||||
|
color: #fce94f
|
||||||
|
}
|
||||||
|
|
||||||
.navbar .navbar-nav .nav-link:hover {
|
.navbar .navbar-nav .nav-link:hover {
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
background-color: #98bcdb;
|
background-color: #98bcdb;
|
||||||
|
@ -678,6 +714,11 @@ chunk, chunk * {
|
||||||
font-size: 12pt;
|
font-size: 12pt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.popuperror {
|
||||||
|
color: #ef2929;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
.popupfooter {
|
.popupfooter {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
|
@ -692,6 +733,12 @@ chunk, chunk * {
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.saveasoverwrite {
|
||||||
|
color: #ff9900;
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
.seqselheader {
|
.seqselheader {
|
||||||
color: #737373;
|
color: #737373;
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,6 +41,8 @@
|
||||||
<a class="dropdown-item" href="#" id="btn_save">Save</a>
|
<a class="dropdown-item" href="#" id="btn_save">Save</a>
|
||||||
<a class="dropdown-item" href="#" id="btn_saveas">Save As</a>
|
<a class="dropdown-item" href="#" id="btn_saveas">Save As</a>
|
||||||
<a class="dropdown-item" href="#" id="btn_savetofile">Save To File...</a>
|
<a class="dropdown-item" href="#" id="btn_savetofile">Save To File...</a>
|
||||||
|
<a class="dropdown-item" href="#" id="btn_download">Download Story as JSON</a>
|
||||||
|
<a class="dropdown-item" href="#" id="btn_downloadtxt">Download Story as Plaintext</a>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item dropdown">
|
<li class="nav-item dropdown">
|
||||||
|
@ -193,7 +195,10 @@
|
||||||
<div class="aidgpopupcontent">
|
<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">
|
||||||
</div>
|
</div>
|
||||||
<div class="hidden" id="saveasoverwrite">
|
<div class="popuperror hidden">
|
||||||
|
<span></span>
|
||||||
|
</div>
|
||||||
|
<div class="saveasoverwrite hidden">
|
||||||
<span>File already exists. Really overwrite?</span>
|
<span>File already exists. Really overwrite?</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="popupfooter">
|
<div class="popupfooter">
|
||||||
|
@ -209,7 +214,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="loadlistheader">
|
<div class="loadlistheader">
|
||||||
<div>Save Name</div>
|
<div>Save Name</div>
|
||||||
<div># Actions</div>
|
<div class="flex-push-right"># Actions</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="loadlistcontent">
|
<div id="loadlistcontent">
|
||||||
</div>
|
</div>
|
||||||
|
@ -219,6 +224,44 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="popupcontainer hidden" id="loadcontainerdelete">
|
||||||
|
<div id="loadpopupdelete">
|
||||||
|
<div class="popuptitlebar">
|
||||||
|
<div class="popuptitletext">Really Delete Story?</div>
|
||||||
|
</div>
|
||||||
|
<div class="dialogheader">
|
||||||
|
"<span id="loadcontainerdelete-storyname"></span>" will be PERMANENTLY deleted! You will not be able to recover this story later.
|
||||||
|
</div>
|
||||||
|
<div class="popuperror hidden">
|
||||||
|
<span></span>
|
||||||
|
</div>
|
||||||
|
<div class="popupfooter">
|
||||||
|
<button type="button" class="btn btn-danger" id="btn_dsaccept">Delete</button>
|
||||||
|
<button type="button" class="btn btn-primary" id="btn_dsclose">Cancel</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="popupcontainer hidden" id="loadcontainerrename">
|
||||||
|
<div id="loadpopuprename">
|
||||||
|
<div class="popuptitlebar">
|
||||||
|
<div class="popuptitletext">Enter New Name For Story</div>
|
||||||
|
</div>
|
||||||
|
<div class="dialogheader">
|
||||||
|
What should the story "<span id="loadcontainerrename-storyname"></span>" be renamed to?
|
||||||
|
<input class="form-control" type="text" placeholder="New Save Name" id="newsavename">
|
||||||
|
</div>
|
||||||
|
<div class="popuperror hidden">
|
||||||
|
<span></span>
|
||||||
|
</div>
|
||||||
|
<div class="saveasoverwrite hidden">
|
||||||
|
<span>File already exists. Really overwrite?</span>
|
||||||
|
</div>
|
||||||
|
<div class="popupfooter">
|
||||||
|
<button type="button" class="btn btn-primary" id="btn_rensaccept">Accept</button>
|
||||||
|
<button type="button" class="btn btn-primary" id="btn_rensclose">Cancel</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="popupcontainer hidden" id="newgamecontainer">
|
<div class="popupcontainer hidden" id="newgamecontainer">
|
||||||
<div id="nspopup">
|
<div id="nspopup">
|
||||||
<div class="popuptitlebar">
|
<div class="popuptitlebar">
|
||||||
|
|
4
utils.py
4
utils.py
|
@ -88,8 +88,8 @@ def addsentencespacing(txt, vars):
|
||||||
# Cleans string for use in file name
|
# Cleans string for use in file name
|
||||||
#==================================================================#
|
#==================================================================#
|
||||||
def cleanfilename(filename):
|
def cleanfilename(filename):
|
||||||
keepcharacters = (' ','.','_')
|
filteredcharacters = ('/','\\')
|
||||||
filename = "".join(c for c in filename if c.isalnum() or c in keepcharacters).rstrip()
|
filename = "".join(c for c in filename if c not in filteredcharacters).rstrip()
|
||||||
return filename
|
return filename
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue