mirror of
https://github.com/KoboldAI/KoboldAI-Client.git
synced 2025-04-17 11:37:33 +02:00
Added Formatting options
Added Bootstrap toggle library for UI Added injection points for input/output modification
This commit is contained in:
parent
0e0947d93a
commit
b55266a7c8
110
aiserver.py
110
aiserver.py
@ -4,15 +4,18 @@
|
||||
# By: KoboldAIDev
|
||||
#==================================================================#
|
||||
|
||||
# External packages
|
||||
from os import path, getcwd
|
||||
import tkinter as tk
|
||||
from tkinter import messagebox
|
||||
import json
|
||||
import torch
|
||||
|
||||
# KoboldAI
|
||||
import fileops
|
||||
import gensettings
|
||||
from utils import debounce
|
||||
import utils
|
||||
|
||||
#==================================================================#
|
||||
# Variables & Storage
|
||||
@ -69,6 +72,7 @@ class vars:
|
||||
hascuda = False # Whether torch has detected CUDA on the system
|
||||
usegpu = False # Whether to launch pipeline with GPU support
|
||||
custmodpth = "" # Filesystem location of custom model to run
|
||||
formatoptns = {} # Container for state of formatting options
|
||||
|
||||
#==================================================================#
|
||||
# Function to get model selection at startup
|
||||
@ -349,6 +353,23 @@ def get_message(msg):
|
||||
vars.andepth = int(msg['data'])
|
||||
emit('from_server', {'cmd': 'setlabelanotedepth', 'data': msg['data']})
|
||||
settingschanged()
|
||||
# Format - Trim incomplete sentences
|
||||
elif(msg['cmd'] == 'frmttriminc'):
|
||||
if('frmttriminc' in vars.formatoptns):
|
||||
vars.formatoptns["frmttriminc"] = msg['data']
|
||||
settingschanged()
|
||||
elif(msg['cmd'] == 'frmtrmblln'):
|
||||
if('frmtrmblln' in vars.formatoptns):
|
||||
vars.formatoptns["frmtrmblln"] = msg['data']
|
||||
settingschanged()
|
||||
elif(msg['cmd'] == 'frmtrmspch'):
|
||||
if('frmtrmspch' in vars.formatoptns):
|
||||
vars.formatoptns["frmtrmspch"] = msg['data']
|
||||
settingschanged()
|
||||
elif(msg['cmd'] == 'frmtadsnsp'):
|
||||
if('frmtadsnsp' in vars.formatoptns):
|
||||
vars.formatoptns["frmtadsnsp"] = msg['data']
|
||||
settingschanged()
|
||||
|
||||
#==================================================================#
|
||||
#
|
||||
@ -368,6 +389,13 @@ def sendsettings():
|
||||
else:
|
||||
for set in gensettings.gensettingsik:
|
||||
emit('from_server', {'cmd': 'addsetting', 'data': set})
|
||||
|
||||
# Send formatting options
|
||||
for frm in gensettings.formatcontrols:
|
||||
emit('from_server', {'cmd': 'addformat', 'data': frm})
|
||||
# Add format key to vars if it wasn't loaded with client.settings
|
||||
if(not frm["id"] in vars.formatoptns):
|
||||
vars.formatoptns[frm["id"]] = False;
|
||||
|
||||
#==================================================================#
|
||||
#
|
||||
@ -375,14 +403,15 @@ def sendsettings():
|
||||
def savesettings():
|
||||
# Build json to write
|
||||
js = {}
|
||||
js["apikey"] = vars.apikey
|
||||
js["andepth"] = vars.andepth
|
||||
js["temp"] = vars.temp
|
||||
js["top_p"] = vars.top_p
|
||||
js["rep_pen"] = vars.rep_pen
|
||||
js["genamt"] = vars.genamt
|
||||
js["max_length"] = vars.max_length
|
||||
js["ikgen"] = vars.ikgen
|
||||
js["apikey"] = vars.apikey
|
||||
js["andepth"] = vars.andepth
|
||||
js["temp"] = vars.temp
|
||||
js["top_p"] = vars.top_p
|
||||
js["rep_pen"] = vars.rep_pen
|
||||
js["genamt"] = vars.genamt
|
||||
js["max_length"] = vars.max_length
|
||||
js["ikgen"] = vars.ikgen
|
||||
js["formatoptns"] = vars.formatoptns
|
||||
|
||||
# Write it
|
||||
file = open("client.settings", "w")
|
||||
@ -417,6 +446,8 @@ def loadsettings():
|
||||
vars.max_length = js["max_length"]
|
||||
if("ikgen" in js):
|
||||
vars.ikgen = js["ikgen"]
|
||||
if("formatoptns" in js):
|
||||
vars.formatoptns = js["formatoptns"]
|
||||
|
||||
file.close()
|
||||
|
||||
@ -436,14 +467,22 @@ def actionsubmit(data):
|
||||
return
|
||||
set_aibusy(1)
|
||||
if(not vars.gamestarted):
|
||||
vars.gamestarted = True # Start the game
|
||||
vars.prompt = data # Save this first action as the prompt
|
||||
emit('from_server', {'cmd': 'updatescreen', 'data': 'Please wait, generating story...'}) # Clear the startup text from game screen
|
||||
# Start the game
|
||||
vars.gamestarted = True
|
||||
# Save this first action as the prompt
|
||||
vars.prompt = data
|
||||
# Clear the startup text from game screen
|
||||
emit('from_server', {'cmd': 'updatescreen', 'data': 'Please wait, generating story...'})
|
||||
calcsubmit(data) # Run the first action through the generator
|
||||
else:
|
||||
# Dont append submission if it's a blank/continue action
|
||||
if(data != ""):
|
||||
# Apply input formatting & scripts before sending to tokenizer
|
||||
data = applyinputformatting(data)
|
||||
# Store the result in the Action log
|
||||
vars.actions.append(data)
|
||||
|
||||
# Off to the tokenizer!
|
||||
calcsubmit(data)
|
||||
|
||||
#==================================================================#
|
||||
@ -594,7 +633,12 @@ def generate(txt, min, max):
|
||||
temperature=vars.temp
|
||||
)[0]["generated_text"]
|
||||
print("{0}{1}{2}".format(colors.CYAN, genout, colors.END))
|
||||
vars.actions.append(getnewcontent(genout))
|
||||
|
||||
# Format output before continuing
|
||||
genout = applyoutputformatting(getnewcontent(genout))
|
||||
|
||||
# Add formatted text to Actions array and refresh the game screen
|
||||
vars.actions.append(genout)
|
||||
refresh_story()
|
||||
emit('from_server', {'cmd': 'texteffect', 'data': len(vars.actions)})
|
||||
|
||||
@ -611,7 +655,7 @@ def formatforhtml(txt):
|
||||
return txt.replace("\\r", "<br/>").replace("\\n", "<br/>").replace('\n', '<br/>').replace('\r', '<br/>')
|
||||
|
||||
#==================================================================#
|
||||
# Strips submitted text from the text returned by the AI
|
||||
# Strips submitted text from the text returned by the AI
|
||||
#==================================================================#
|
||||
def getnewcontent(txt):
|
||||
ln = len(vars.actions)
|
||||
@ -622,6 +666,35 @@ def getnewcontent(txt):
|
||||
|
||||
return (txt.split(delim)[-1])
|
||||
|
||||
#==================================================================#
|
||||
# Applies chosen formatting options to text submitted to AI
|
||||
#==================================================================#
|
||||
def applyinputformatting(txt):
|
||||
# Add sentence spacing
|
||||
if(vars.formatoptns["frmtadsnsp"]):
|
||||
txt = utils.addsentencespacing(txt, vars.actions)
|
||||
|
||||
return txt
|
||||
|
||||
#==================================================================#
|
||||
# Applies chosen formatting options to text returned from AI
|
||||
#==================================================================#
|
||||
def applyoutputformatting(txt):
|
||||
# Use standard quotes and apostrophes
|
||||
txt = utils.fixquotes(txt)
|
||||
|
||||
# Trim incomplete sentences
|
||||
if(vars.formatoptns["frmttriminc"]):
|
||||
txt = utils.trimincompletesentence(txt)
|
||||
# Replace blank lines
|
||||
if(vars.formatoptns["frmtrmblln"]):
|
||||
txt = utils.replaceblanklines(txt)
|
||||
# Remove special characters
|
||||
if(vars.formatoptns["frmtrmspch"]):
|
||||
txt = utils.removespecialchars(txt)
|
||||
|
||||
return txt
|
||||
|
||||
#==================================================================#
|
||||
# Sends the current story content to the Game Screen
|
||||
#==================================================================#
|
||||
@ -637,6 +710,9 @@ def refresh_story():
|
||||
# Sends the current generator settings to the Game Menu
|
||||
#==================================================================#
|
||||
def refresh_settings():
|
||||
# Suppress toggle change events while loading state
|
||||
emit('from_server', {'cmd': 'allowtoggle', 'data': False})
|
||||
|
||||
if(vars.model != "InferKit"):
|
||||
emit('from_server', {'cmd': 'updatetemp', 'data': vars.temp})
|
||||
emit('from_server', {'cmd': 'updatetopp', 'data': vars.top_p})
|
||||
@ -649,6 +725,14 @@ def refresh_settings():
|
||||
emit('from_server', {'cmd': 'updateikgen', 'data': vars.ikgen})
|
||||
|
||||
emit('from_server', {'cmd': 'updateanotedepth', 'data': vars.andepth})
|
||||
|
||||
emit('from_server', {'cmd': 'updatefrmttriminc', 'data': vars.formatoptns["frmttriminc"]})
|
||||
emit('from_server', {'cmd': 'updatefrmtrmblln', 'data': vars.formatoptns["frmtrmblln"]})
|
||||
emit('from_server', {'cmd': 'updatefrmtrmspch', 'data': vars.formatoptns["frmtrmspch"]})
|
||||
emit('from_server', {'cmd': 'updatefrmtadsnsp', 'data': vars.formatoptns["frmtadsnsp"]})
|
||||
|
||||
# Allow toggle events again
|
||||
emit('from_server', {'cmd': 'allowtoggle', 'data': True})
|
||||
|
||||
#==================================================================#
|
||||
# Sets the logical and display states for the AI Busy condition
|
||||
|
@ -86,4 +86,25 @@ gensettingsik =[{
|
||||
"step": 2,
|
||||
"default": 200,
|
||||
"tooltip": "Number of characters the AI should generate."
|
||||
}]
|
||||
}]
|
||||
|
||||
formatcontrols = [{
|
||||
"label": "Trim incomplete sentences",
|
||||
"id": "frmttriminc",
|
||||
"tooltip": "Remove text after last sentence closure. If no closure is found, all tokens will be returned."
|
||||
},
|
||||
{
|
||||
"label": "Remove blank lines",
|
||||
"id": "frmtrmblln",
|
||||
"tooltip": "Replace double newlines (\\n\\n) with single newlines to avoid blank lines."
|
||||
},
|
||||
{
|
||||
"label": "Remove special characters",
|
||||
"id": "frmtrmspch",
|
||||
"tooltip": "Remove special characters (@,#,%,^, etc)"
|
||||
},
|
||||
{
|
||||
"label": "Add sentence spacing",
|
||||
"id": "frmtadsnsp",
|
||||
"tooltip": "If the last action ended with punctuation, add a space to the beginning of the next action."
|
||||
}]
|
@ -11,6 +11,7 @@ var button_newgame;
|
||||
var button_save;
|
||||
var button_load;
|
||||
var button_settings;
|
||||
var button_format;
|
||||
var button_send;
|
||||
var button_actedit;
|
||||
var button_actmem;
|
||||
@ -21,6 +22,7 @@ var game_text;
|
||||
var input_text;
|
||||
var message_text;
|
||||
var settings_menu;
|
||||
var format_menu;
|
||||
var anote_menu;
|
||||
var anote_input;
|
||||
var anote_labelcur;
|
||||
@ -30,6 +32,10 @@ var anote_slider;
|
||||
var shift_down = false;
|
||||
var do_clear_ent = false;
|
||||
|
||||
// Display vars
|
||||
var allowtoggle = false;
|
||||
var formatcount = 0;
|
||||
|
||||
//=================================================================//
|
||||
// METHODS
|
||||
//=================================================================//
|
||||
@ -58,16 +64,44 @@ function addSetting(ob) {
|
||||
</div>\
|
||||
</div>");
|
||||
// Set references to HTML objects
|
||||
refin = $("#"+ob.id);
|
||||
reflb = $("#"+ob.id+"cur");
|
||||
window["setting_"+ob.id] = refin;
|
||||
window["label_"+ob.id] = reflb;
|
||||
var refin = $("#"+ob.id);
|
||||
var reflb = $("#"+ob.id+"cur");
|
||||
window["setting_"+ob.id] = refin; // Is this still needed?
|
||||
window["label_"+ob.id] = reflb; // Is this still needed?
|
||||
// Add event function to input
|
||||
refin.on("input", function () {
|
||||
socket.send({'cmd': $(this).attr('id'), 'data': $(this).val()});
|
||||
});
|
||||
}
|
||||
|
||||
function addFormat(ob) {
|
||||
// Check if we need to make a new column for this button
|
||||
if(formatcount == 0) {
|
||||
format_menu.append("<div class=\"formatcolumn\"></div>");
|
||||
}
|
||||
// Get reference to the last child column
|
||||
var ref = $("#formatmenu > div").last();
|
||||
// Add format block to Format Menu
|
||||
ref.append("<div class=\"formatrow\">\
|
||||
<input type=\"checkbox\" data-toggle=\"toggle\" data-onstyle=\"success\" id=\""+ob.id+"\">\
|
||||
<span class=\"formatlabel\">"+ob.label+" </span>\
|
||||
<span class=\"helpicon\">?<span class=\"helptext\">"+ob.tooltip+"</span></span>\
|
||||
</div>");
|
||||
// Tell Bootstrap-Toggle to render the new checkbox
|
||||
$("input[type=checkbox]").bootstrapToggle();
|
||||
// Add event to input
|
||||
$("#"+ob.id).on("change", function () {
|
||||
if(allowtoggle) {
|
||||
socket.send({'cmd': $(this).attr('id'), 'data': $(this).prop('checked')});
|
||||
}
|
||||
});
|
||||
// Increment display variable
|
||||
formatcount++;
|
||||
if(formatcount == 2) {
|
||||
formatcount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
function enableButtons(refs) {
|
||||
for(i=0; i<refs.length; i++) {
|
||||
refs[i].prop("disabled",false);
|
||||
@ -207,6 +241,7 @@ $(document).ready(function(){
|
||||
button_save = $('#btn_save');
|
||||
button_load = $('#btn_load');
|
||||
button_settings = $('#btn_settings');
|
||||
button_format = $('#btn_format');
|
||||
button_send = $('#btnsend');
|
||||
button_actedit = $('#btn_actedit');
|
||||
button_actmem = $('#btn_actmem');
|
||||
@ -217,6 +252,7 @@ $(document).ready(function(){
|
||||
input_text = $('#input_text');
|
||||
message_text = $('#messagefield');
|
||||
settings_menu = $("#settingsmenu");
|
||||
format_menu = $('#formatmenu');
|
||||
anote_menu = $('#anoterowcontainer');
|
||||
anote_input = $('#anoteinput');
|
||||
anote_labelcur = $('#anotecur');
|
||||
@ -233,6 +269,7 @@ $(document).ready(function(){
|
||||
connect_status.addClass("color_green");
|
||||
// Reset Settings Menu
|
||||
settings_menu.html("");
|
||||
format_menu.html("");
|
||||
} else if(msg.cmd == "updatescreen") {
|
||||
// Send game content to Game Screen
|
||||
game_text.html(msg.data);
|
||||
@ -340,6 +377,24 @@ $(document).ready(function(){
|
||||
} else if(msg.cmd == "addsetting") {
|
||||
// Add setting controls
|
||||
addSetting(msg.data);
|
||||
} else if(msg.cmd == "addformat") {
|
||||
// Add setting controls
|
||||
addFormat(msg.data);
|
||||
} else if(msg.cmd == "updatefrmttriminc") {
|
||||
// Update toggle state
|
||||
$("#frmttriminc").prop('checked', msg.data).change()
|
||||
} else if(msg.cmd == "updatefrmtrmblln") {
|
||||
// Update toggle state
|
||||
$("#frmtrmblln").prop('checked', msg.data).change()
|
||||
} else if(msg.cmd == "updatefrmtrmspch") {
|
||||
// Update toggle state
|
||||
$("#frmtrmspch").prop('checked', msg.data).change()
|
||||
} else if(msg.cmd == "updatefrmtadsnsp") {
|
||||
// Update toggle state
|
||||
$("#frmtadsnsp").prop('checked', msg.data).change()
|
||||
} else if(msg.cmd == "allowtoggle") {
|
||||
// Allow toggle change states to propagate
|
||||
allowtoggle = msg.data;
|
||||
}
|
||||
});
|
||||
|
||||
@ -390,6 +445,10 @@ $(document).ready(function(){
|
||||
$('#settingsmenu').slideToggle("slow");
|
||||
});
|
||||
|
||||
button_format.on("click", function(ev) {
|
||||
$('#formatmenu').slideToggle("slow");
|
||||
});
|
||||
|
||||
$("#btn_savesettings").on("click", function(ev) {
|
||||
socket.send({'cmd': 'savesettings', 'data': ''});
|
||||
});
|
||||
|
28
static/bootstrap-toggle.min.css
vendored
Normal file
28
static/bootstrap-toggle.min.css
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
/*! ========================================================================
|
||||
* Bootstrap Toggle: bootstrap-toggle.css v2.2.0
|
||||
* http://www.bootstraptoggle.com
|
||||
* ========================================================================
|
||||
* Copyright 2014 Min Hur, The New York Times Company
|
||||
* Licensed under MIT
|
||||
* ======================================================================== */
|
||||
.checkbox label .toggle,.checkbox-inline .toggle{margin-left:-20px;margin-right:5px}
|
||||
.toggle{position:relative;overflow:hidden}
|
||||
.toggle input[type=checkbox]{display:none}
|
||||
.toggle-group{position:absolute;width:200%;top:0;bottom:0;left:0;transition:left .35s;-webkit-transition:left .35s;-moz-user-select:none;-webkit-user-select:none}
|
||||
.toggle.off .toggle-group{left:-100%}
|
||||
.toggle-on{position:absolute;top:0;bottom:0;left:0;right:50%;margin:0;border:0;border-radius:0}
|
||||
.toggle-off{position:absolute;top:0;bottom:0;left:50%;right:0;margin:0;border:0;border-radius:0}
|
||||
.toggle-handle{position:relative;margin:0 auto;padding-top:0;padding-bottom:0;height:100%;width:0;border-width:0 1px}
|
||||
.toggle.btn{min-width:59px;min-height:34px}
|
||||
.toggle-on.btn{padding-right:24px}
|
||||
.toggle-off.btn{padding-left:24px}
|
||||
.toggle.btn-lg{min-width:79px;min-height:45px}
|
||||
.toggle-on.btn-lg{padding-right:31px}
|
||||
.toggle-off.btn-lg{padding-left:31px}
|
||||
.toggle-handle.btn-lg{width:40px}
|
||||
.toggle.btn-sm{min-width:50px;min-height:30px}
|
||||
.toggle-on.btn-sm{padding-right:20px}
|
||||
.toggle-off.btn-sm{padding-left:20px}
|
||||
.toggle.btn-xs{min-width:35px;min-height:22px}
|
||||
.toggle-on.btn-xs{padding-right:12px}
|
||||
.toggle-off.btn-xs{padding-left:12px}
|
9
static/bootstrap-toggle.min.js
vendored
Normal file
9
static/bootstrap-toggle.min.js
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
/*! ========================================================================
|
||||
* Bootstrap Toggle: bootstrap-toggle.js v2.2.0
|
||||
* http://www.bootstraptoggle.com
|
||||
* ========================================================================
|
||||
* Copyright 2014 Min Hur, The New York Times Company
|
||||
* Licensed under MIT
|
||||
* ======================================================================== */
|
||||
+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.toggle"),f="object"==typeof b&&b;e||d.data("bs.toggle",e=new c(this,f)),"string"==typeof b&&e[b]&&e[b]()})}var c=function(b,c){this.$element=a(b),this.options=a.extend({},this.defaults(),c),this.render()};c.VERSION="2.2.0",c.DEFAULTS={on:"On",off:"Off",onstyle:"primary",offstyle:"default",size:"normal",style:"",width:null,height:null},c.prototype.defaults=function(){return{on:this.$element.attr("data-on")||c.DEFAULTS.on,off:this.$element.attr("data-off")||c.DEFAULTS.off,onstyle:this.$element.attr("data-onstyle")||c.DEFAULTS.onstyle,offstyle:this.$element.attr("data-offstyle")||c.DEFAULTS.offstyle,size:this.$element.attr("data-size")||c.DEFAULTS.size,style:this.$element.attr("data-style")||c.DEFAULTS.style,width:this.$element.attr("data-width")||c.DEFAULTS.width,height:this.$element.attr("data-height")||c.DEFAULTS.height}},c.prototype.render=function(){this._onstyle="btn-"+this.options.onstyle,this._offstyle="btn-"+this.options.offstyle;var b="large"===this.options.size?"btn-lg":"small"===this.options.size?"btn-sm":"mini"===this.options.size?"btn-xs":"",c=a('<label class="btn">').html(this.options.on).addClass(this._onstyle+" "+b),d=a('<label class="btn">').html(this.options.off).addClass(this._offstyle+" "+b+" active"),e=a('<span class="toggle-handle btn btn-default">').addClass(b),f=a('<div class="toggle-group">').append(c,d,e),g=a('<div class="toggle btn" data-toggle="toggle">').addClass(this.$element.prop("checked")?this._onstyle:this._offstyle+" off").addClass(b).addClass(this.options.style);this.$element.wrap(g),a.extend(this,{$toggle:this.$element.parent(),$toggleOn:c,$toggleOff:d,$toggleGroup:f}),this.$toggle.append(f);var h=this.options.width||Math.max(c.outerWidth(),d.outerWidth())+e.outerWidth()/2,i=this.options.height||Math.max(c.outerHeight(),d.outerHeight());c.addClass("toggle-on"),d.addClass("toggle-off"),this.$toggle.css({width:h,height:i}),this.options.height&&(c.css("line-height",c.height()+"px"),d.css("line-height",d.height()+"px")),this.update(!0),this.trigger(!0)},c.prototype.toggle=function(){this.$element.prop("checked")?this.off():this.on()},c.prototype.on=function(a){return this.$element.prop("disabled")?!1:(this.$toggle.removeClass(this._offstyle+" off").addClass(this._onstyle),this.$element.prop("checked",!0),void(a||this.trigger()))},c.prototype.off=function(a){return this.$element.prop("disabled")?!1:(this.$toggle.removeClass(this._onstyle).addClass(this._offstyle+" off"),this.$element.prop("checked",!1),void(a||this.trigger()))},c.prototype.enable=function(){this.$toggle.removeAttr("disabled"),this.$element.prop("disabled",!1)},c.prototype.disable=function(){this.$toggle.attr("disabled","disabled"),this.$element.prop("disabled",!0)},c.prototype.update=function(a){this.$element.prop("disabled")?this.disable():this.enable(),this.$element.prop("checked")?this.on(a):this.off(a)},c.prototype.trigger=function(b){this.$element.off("change.bs.toggle"),b||this.$element.change(),this.$element.on("change.bs.toggle",a.proxy(function(){this.update()},this))},c.prototype.destroy=function(){this.$element.off("change.bs.toggle"),this.$toggleGroup.remove(),this.$element.removeData("bs.toggle"),this.$element.unwrap()};var d=a.fn.bootstrapToggle;a.fn.bootstrapToggle=b,a.fn.bootstrapToggle.Constructor=c,a.fn.toggle.noConflict=function(){return a.fn.bootstrapToggle=d,this},a(function(){a("input[type=checkbox][data-toggle^=toggle]").bootstrapToggle()}),a(document).on("click.bs.toggle","div[data-toggle^=toggle]",function(b){var c=a(this).find("input[type=checkbox]");c.bootstrapToggle("toggle"),b.preventDefault()})}(jQuery);
|
||||
//# sourceMappingURL=bootstrap-toggle.min.js.map
|
@ -22,6 +22,12 @@ chunk {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
#formatmenu {
|
||||
display:none;
|
||||
background-color: #295071;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
#connectstatusdiv {
|
||||
display: flex;
|
||||
}
|
||||
@ -99,7 +105,7 @@ chunk {
|
||||
|
||||
#waitanim {
|
||||
position:absolute;
|
||||
top:10px;
|
||||
top:18px;
|
||||
left:5px;
|
||||
}
|
||||
|
||||
@ -166,6 +172,26 @@ chunk {
|
||||
color: #ff0000;
|
||||
}
|
||||
|
||||
.formatcolumn {
|
||||
width: 25%;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.formatcolumn > div:first-child {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.formatrow {
|
||||
|
||||
}
|
||||
|
||||
.formatlabel {
|
||||
color: #ffffff;
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
@ -8,8 +8,10 @@
|
||||
<script src="static/socket.io.min.js"></script>
|
||||
<script src="static/application.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>
|
||||
<script src="static/bootstrap-toggle.min.js"></script>
|
||||
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="static/bootstrap-toggle.min.css">
|
||||
<link rel="stylesheet" href="static/custom.css">
|
||||
</head>
|
||||
<body>
|
||||
@ -22,6 +24,7 @@
|
||||
<button type="button" class="btn btn-primary" id="btn_load">Load</button>
|
||||
<div class="spacer"></div>
|
||||
<button type="button" class="btn btn-primary" id="btn_settings">Settings</button>
|
||||
<button type="button" class="btn btn-primary" id="btn_format">Formatting</button>
|
||||
</div>
|
||||
<div id="connectstatusdiv">
|
||||
<span id="connectstatus" class="color_orange">Waiting for connection...</span>
|
||||
@ -30,6 +33,8 @@
|
||||
</div>
|
||||
<div class="row" id="settingsmenu">
|
||||
</div>
|
||||
<div class="row" id="formatmenu">
|
||||
</div>
|
||||
<div class="row" id="gamescreen">
|
||||
<span id="gametext">...</span>
|
||||
</div>
|
||||
|
66
utils.py
66
utils.py
@ -1,5 +1,10 @@
|
||||
from threading import Timer
|
||||
import re
|
||||
|
||||
#==================================================================#
|
||||
# Decorator to prevent a function's actions from being run until
|
||||
# at least x seconds have passed without the function being called
|
||||
#==================================================================#
|
||||
def debounce(wait):
|
||||
def decorator(fun):
|
||||
def debounced(*args, **kwargs):
|
||||
@ -16,4 +21,63 @@ def debounce(wait):
|
||||
|
||||
return debounced
|
||||
|
||||
return decorator
|
||||
return decorator
|
||||
|
||||
#==================================================================#
|
||||
# Replace fancy quotes and apostrope's with standard ones
|
||||
#==================================================================#
|
||||
def fixquotes(txt):
|
||||
txt = txt.replace("“", '"')
|
||||
txt = txt.replace("”", '"')
|
||||
txt = txt.replace("’", "'")
|
||||
txt = txt.replace("`", "'")
|
||||
return txt
|
||||
|
||||
#==================================================================#
|
||||
#
|
||||
#==================================================================#
|
||||
def trimincompletesentence(txt):
|
||||
# Cache length of text
|
||||
ln = len(txt)
|
||||
# Find last instance of punctuation (Borrowed from Clover-Edition by cloveranon)
|
||||
lastpunc = max(txt.rfind("."), txt.rfind("!"), txt.rfind("?"))
|
||||
# Is this the end of a quote?
|
||||
if(lastpunc < ln-1):
|
||||
if(txt[lastpunc+1] == '"'):
|
||||
lastpunc = lastpunc + 1
|
||||
if(lastpunc >= 0):
|
||||
txt = txt[:lastpunc+1]
|
||||
return txt
|
||||
|
||||
#==================================================================#
|
||||
#
|
||||
#==================================================================#
|
||||
def replaceblanklines(txt):
|
||||
txt = txt.replace("\n\n", "\n")
|
||||
return txt
|
||||
|
||||
#==================================================================#
|
||||
#
|
||||
#==================================================================#
|
||||
def removespecialchars(txt):
|
||||
txt = re.sub(r"[#/@%<>{}+=~|\^]", "", txt)
|
||||
return txt
|
||||
|
||||
#==================================================================#
|
||||
# If the next action follows a sentence closure, add a space
|
||||
#==================================================================#
|
||||
def addsentencespacing(txt, acts):
|
||||
# Get last character of last action
|
||||
lastchar = acts[-1][-1]
|
||||
if(lastchar == "." or lastchar == "!" or lastchar == "?"):
|
||||
txt = " " + txt
|
||||
return txt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user