Added support for Author's Note
Increased input textarea height Removed generator options from save/load system Set output length slider to use steps of 2
This commit is contained in:
parent
0ce77f4875
commit
229b10cb91
142
aiserver.py
142
aiserver.py
|
@ -40,20 +40,22 @@ modellist = [
|
|||
# Variables
|
||||
class vars:
|
||||
lastact = "" # The last action submitted to the generator
|
||||
model = ''
|
||||
model = ""
|
||||
noai = False # Runs the script without starting up the transformers pipeline
|
||||
aibusy = False # Stops submissions while the AI is working
|
||||
max_length = 500 # Maximum number of tokens to submit per action
|
||||
genamt = 60 # Amount of text for each action to generate
|
||||
rep_pen = 1.0 # Generator repetition_penalty
|
||||
temp = 0.9 # Generator temperature
|
||||
top_p = 1.0 # Generator top_p
|
||||
rep_pen = 1.0 # Default generator repetition_penalty
|
||||
temp = 0.9 # Default generator temperature
|
||||
top_p = 1.0 # Default generator top_p
|
||||
gamestarted = False
|
||||
prompt = ""
|
||||
memory = ""
|
||||
authornote = ""
|
||||
andepth = 3 # How far back in history to append author's note
|
||||
actions = []
|
||||
mode = "play" # Whether the interface is in play, memory, or edit mode
|
||||
editln = 0 # Which line was last selected in Edit Mode
|
||||
editln = 0 # Which line was last selected in Edit Mode
|
||||
url = "https://api.inferkit.com/v1/models/standard/generate" # InferKit API URL
|
||||
apikey = "" # API key to use for InferKit API calls
|
||||
savedir = getcwd()+"\stories\\newstory.json"
|
||||
|
@ -303,6 +305,13 @@ def get_message(msg):
|
|||
elif(msg['cmd'] == 'setoutput'):
|
||||
vars.genamt = int(msg['data'])
|
||||
emit('from_server', {'cmd': 'setlabeloutput', 'data': msg['data']})
|
||||
# Author's Note field update
|
||||
elif(msg['cmd'] == 'anote'):
|
||||
anotesubmit(msg['data'])
|
||||
# Author's Note depth update
|
||||
elif(msg['cmd'] == 'anotedepth'):
|
||||
vars.andepth = int(msg['data'])
|
||||
emit('from_server', {'cmd': 'setlabelanotedepth', 'data': msg['data']})
|
||||
|
||||
#==================================================================#
|
||||
#
|
||||
|
@ -333,9 +342,20 @@ def actionsubmit(data):
|
|||
# Take submitted text and build the text to be given to generator
|
||||
#==================================================================#
|
||||
def calcsubmit(txt):
|
||||
vars.lastact = txt # Store most recent action in memory (is this still needed?)
|
||||
anotetxt = "" # Placeholder for Author's Note text
|
||||
lnanote = 0 # Placeholder for Author's Note length
|
||||
forceanote = False # In case we don't have enough actions to hit A.N. depth
|
||||
anoteadded = False # In case our budget runs out before we hit A.N. depth
|
||||
actionlen = len(vars.actions)
|
||||
|
||||
# Build Author's Note if set
|
||||
if(vars.authornote != ""):
|
||||
anotetxt = "\n[Author's note: "+vars.authornote+"]\n"
|
||||
|
||||
# For all transformers models
|
||||
if(vars.model != "InferKit"):
|
||||
vars.lastact = txt # Store most recent action in memory (is this still needed?)
|
||||
anotetkns = [] # Placeholder for Author's Note tokens
|
||||
|
||||
# Calculate token budget
|
||||
prompttkns = tokenizer.encode(vars.prompt)
|
||||
|
@ -344,17 +364,28 @@ def calcsubmit(txt):
|
|||
memtokens = tokenizer.encode(vars.memory)
|
||||
lnmem = len(memtokens)
|
||||
|
||||
budget = vars.max_length - lnprompt - lnmem - vars.genamt
|
||||
if(anotetxt != ""):
|
||||
anotetkns = tokenizer.encode(anotetxt)
|
||||
lnanote = len(anotetkns)
|
||||
|
||||
if(len(vars.actions) == 0):
|
||||
budget = vars.max_length - lnprompt - lnmem - lnanote - vars.genamt
|
||||
|
||||
if(actionlen == 0):
|
||||
# First/Prompt action
|
||||
subtxt = vars.memory + vars.prompt
|
||||
lnsub = len(memtokens+prompttkns)
|
||||
subtxt = vars.memory + anotetxt + vars.prompt
|
||||
lnsub = lnmem + lnprompt + lnanote
|
||||
|
||||
generate(subtxt, lnsub+1, lnsub+vars.genamt)
|
||||
else:
|
||||
tokens = []
|
||||
|
||||
# Check if we have the action depth to hit our A.N. depth
|
||||
if(anotetxt != "" and actionlen < vars.andepth):
|
||||
forceanote = True
|
||||
|
||||
# Get most recent action tokens up to our budget
|
||||
tokens = []
|
||||
for n in range(len(vars.actions)):
|
||||
for n in range(actionlen):
|
||||
|
||||
if(budget <= 0):
|
||||
break
|
||||
acttkns = tokenizer.encode(vars.actions[(-1-n)])
|
||||
|
@ -366,9 +397,22 @@ def calcsubmit(txt):
|
|||
count = budget * -1
|
||||
tokens = acttkns[count:] + tokens
|
||||
break
|
||||
|
||||
# Add mmory & prompt tokens to beginning of bundle
|
||||
tokens = memtokens + prompttkns + tokens
|
||||
|
||||
# Inject Author's Note if we've reached the desired depth
|
||||
if(n == vars.andepth-1):
|
||||
if(anotetxt != ""):
|
||||
tokens = anotetkns + tokens # A.N. len already taken from bdgt
|
||||
anoteadded = True
|
||||
|
||||
# Did we get to add the A.N.? If not, do it here
|
||||
if(anotetxt != ""):
|
||||
if((not anoteadded) or forceanote):
|
||||
tokens = memtokens + anotetkns + prompttkns + tokens
|
||||
else:
|
||||
tokens = memtokens + prompttkns + tokens
|
||||
else:
|
||||
# Prepend Memory and Prompt before action tokens
|
||||
tokens = memtokens + prompttkns + tokens
|
||||
|
||||
# Send completed bundle to generator
|
||||
ln = len(tokens)
|
||||
|
@ -379,9 +423,15 @@ def calcsubmit(txt):
|
|||
)
|
||||
# For InferKit web API
|
||||
else:
|
||||
budget = vars.max_length - len(vars.prompt) - len(vars.memory) - 1
|
||||
|
||||
# Check if we have the action depth to hit our A.N. depth
|
||||
if(anotetxt != "" and actionlen < vars.andepth):
|
||||
forceanote = True
|
||||
|
||||
budget = vars.max_length - len(vars.prompt) - len(anotetxt) - len(vars.memory) - 1
|
||||
subtxt = ""
|
||||
for n in range(len(vars.actions)):
|
||||
for n in range(actionlen):
|
||||
|
||||
if(budget <= 0):
|
||||
break
|
||||
actlen = len(vars.actions[(-1-n)])
|
||||
|
@ -392,12 +442,26 @@ def calcsubmit(txt):
|
|||
count = budget * -1
|
||||
subtxt = vars.actions[(-1-n)][count:] + subtxt
|
||||
break
|
||||
|
||||
# Inject Author's Note if we've reached the desired depth
|
||||
if(n == vars.andepth-1):
|
||||
if(anotetxt != ""):
|
||||
subtxt = anotetxt + subtxt # A.N. len already taken from bdgt
|
||||
anoteadded = True
|
||||
|
||||
# Add mmory & prompt tokens to beginning of bundle
|
||||
# Format memory for inclusion (adding newline separator)
|
||||
memsub = ""
|
||||
if(vars.memory != ""):
|
||||
subtxt = vars.memory + "\n" + vars.prompt + subtxt
|
||||
memsub = vars.memory + "\n"
|
||||
|
||||
# Did we get to add the A.N.? If not, do it here
|
||||
if(anotetxt != ""):
|
||||
if((not anoteadded) or forceanote):
|
||||
subtxt = memsub + anotetxt + vars.prompt + subtxt
|
||||
else:
|
||||
subtxt = memsub + vars.prompt + subtxt
|
||||
else:
|
||||
subtxt = vars.prompt + subtxt
|
||||
subtxt = memsub + vars.prompt + subtxt
|
||||
|
||||
# Send it!
|
||||
ikrequest(subtxt)
|
||||
|
@ -407,7 +471,12 @@ def calcsubmit(txt):
|
|||
#==================================================================#
|
||||
def generate(txt, min, max):
|
||||
print("{0}Min:{1}, Max:{2}, Txt:{3}{4}".format(colors.WARNING, min, max, txt, colors.ENDC))
|
||||
|
||||
|
||||
# Clear CUDA cache if using GPU
|
||||
if(vars.hascuda and vars.usegpu):
|
||||
torch.cuda.empty_cache()
|
||||
|
||||
# Submit input text to generator
|
||||
genout = generator(
|
||||
txt,
|
||||
do_sample=True,
|
||||
|
@ -421,6 +490,10 @@ def generate(txt, min, max):
|
|||
refresh_story()
|
||||
emit('from_server', {'cmd': 'texteffect', 'data': len(vars.actions)})
|
||||
|
||||
# Clear CUDA cache again if using GPU
|
||||
if(vars.hascuda and vars.usegpu):
|
||||
torch.cuda.empty_cache()
|
||||
|
||||
set_aibusy(0)
|
||||
|
||||
#==================================================================#
|
||||
|
@ -460,6 +533,7 @@ def refresh_settings():
|
|||
emit('from_server', {'cmd': 'updatetopp', 'data': vars.top_p})
|
||||
emit('from_server', {'cmd': 'updatereppen', 'data': vars.rep_pen})
|
||||
emit('from_server', {'cmd': 'updateoutlen', 'data': vars.genamt})
|
||||
emit('from_server', {'cmd': 'updatanotedepth', 'data': vars.andepth})
|
||||
|
||||
#==================================================================#
|
||||
# Sets the logical and display states for the AI Busy condition
|
||||
|
@ -521,6 +595,7 @@ def togglememorymode():
|
|||
vars.mode = "memory"
|
||||
emit('from_server', {'cmd': 'memmode', 'data': 'true'})
|
||||
emit('from_server', {'cmd': 'setinputtext', 'data': vars.memory})
|
||||
emit('from_server', {'cmd': 'setanote', 'data': vars.authornote})
|
||||
elif(vars.mode == "memory"):
|
||||
vars.mode = "play"
|
||||
emit('from_server', {'cmd': 'memmode', 'data': 'false'})
|
||||
|
@ -534,6 +609,17 @@ def memsubmit(data):
|
|||
vars.memory = data
|
||||
vars.mode = "play"
|
||||
emit('from_server', {'cmd': 'memmode', 'data': 'false'})
|
||||
|
||||
# Ask for contents of Author's Note field
|
||||
emit('from_server', {'cmd': 'getanote', 'data': ''})
|
||||
|
||||
#==================================================================#
|
||||
# Commit changes to Author's Note
|
||||
#==================================================================#
|
||||
def anotesubmit(data):
|
||||
# Maybe check for length at some point
|
||||
# For now just send it to storage
|
||||
vars.authornote = data
|
||||
|
||||
#==================================================================#
|
||||
# Assembles game data into a request to InferKit API
|
||||
|
@ -611,11 +697,12 @@ def saveRequest():
|
|||
js = {}
|
||||
#js["maxlegth"] = vars.max_length # This causes problems when switching to/from InfraKit
|
||||
#js["genamt"] = vars.genamt
|
||||
js["rep_pen"] = vars.rep_pen
|
||||
js["temp"] = vars.temp
|
||||
#js["rep_pen"] = vars.rep_pen
|
||||
#js["temp"] = vars.temp
|
||||
js["gamestarted"] = vars.gamestarted
|
||||
js["prompt"] = vars.prompt
|
||||
js["memory"] = vars.memory
|
||||
js["authorsnote"] = vars.authornote
|
||||
js["actions"] = vars.actions
|
||||
js["savedir"] = path
|
||||
# Write it
|
||||
|
@ -638,13 +725,18 @@ def loadRequest():
|
|||
# Copy file contents to vars
|
||||
#vars.max_length = js["maxlegth"] # This causes problems when switching to/from InfraKit
|
||||
#vars.genamt = js["genamt"]
|
||||
vars.rep_pen = js["rep_pen"]
|
||||
vars.temp = js["temp"]
|
||||
#vars.rep_pen = js["rep_pen"]
|
||||
#vars.temp = js["temp"]
|
||||
vars.gamestarted = js["gamestarted"]
|
||||
vars.prompt = js["prompt"]
|
||||
vars.memory = js["memory"]
|
||||
vars.actions = js["actions"]
|
||||
vars.savedir = js["savedir"]
|
||||
|
||||
# Try not to break older save files
|
||||
if("authorsnote" in js):
|
||||
vars.authornote = js["authorsnote"]
|
||||
|
||||
file.close()
|
||||
# Refresh game screen
|
||||
refresh_story()
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
//=================================================================//
|
||||
// VARIABLES
|
||||
//=================================================================//
|
||||
|
||||
// Socket IO Object
|
||||
var socket;
|
||||
|
||||
// UI references for jQuery
|
||||
var button_newgame;
|
||||
var button_save;
|
||||
var button_load;
|
||||
|
@ -20,33 +23,46 @@ var setting_temp;
|
|||
var setting_topp;
|
||||
var setting_reppen;
|
||||
var setting_outlen;
|
||||
var label_temp;
|
||||
var label_topp;
|
||||
var label_reppen;
|
||||
var label_outlen;
|
||||
var anote_menu;
|
||||
var anote_input;
|
||||
var anote_labelcur;
|
||||
var anote_slider;
|
||||
|
||||
var shift_down = false;
|
||||
// Key states
|
||||
var shift_down = false;
|
||||
var do_clear_ent = false;
|
||||
|
||||
//=================================================================//
|
||||
// METHODS
|
||||
//=================================================================//
|
||||
|
||||
function enableButton(ref) {
|
||||
ref.prop("disabled",false);
|
||||
ref.removeClass("btn-secondary");
|
||||
ref.addClass("btn-primary");
|
||||
function enableButtons(refs) {
|
||||
for(i=0; i<refs.length; i++) {
|
||||
refs[i].prop("disabled",false);
|
||||
refs[i].removeClass("btn-secondary");
|
||||
refs[i].addClass("btn-primary");
|
||||
}
|
||||
}
|
||||
|
||||
function disableButton(ref) {
|
||||
ref.prop("disabled",true);
|
||||
ref.removeClass("btn-primary");
|
||||
ref.addClass("btn-secondary");
|
||||
function disableButtons(refs) {
|
||||
for(i=0; i<refs.length; i++) {
|
||||
refs[i].prop("disabled",true);
|
||||
refs[i].removeClass("btn-primary");
|
||||
refs[i].addClass("btn-secondary");
|
||||
}
|
||||
}
|
||||
|
||||
function enableSendBtn() {
|
||||
enableButton(button_send)
|
||||
enableButtons([button_send])
|
||||
button_send.html("Submit");
|
||||
}
|
||||
|
||||
function disableSendBtn() {
|
||||
disableButton(button_send)
|
||||
disableButtons([button_send])
|
||||
button_send.html("");
|
||||
}
|
||||
|
||||
|
@ -75,12 +91,16 @@ function hideWaitAnimation() {
|
|||
$('#waitanim').remove();
|
||||
}
|
||||
|
||||
function hide(ref) {
|
||||
ref.addClass("hidden");
|
||||
function hide(refs) {
|
||||
for(i=0; i<refs.length; i++) {
|
||||
refs[i].addClass("hidden");
|
||||
}
|
||||
}
|
||||
|
||||
function show(ref) {
|
||||
ref.removeClass("hidden");
|
||||
function show(refs) {
|
||||
for(i=0; i<refs.length; i++) {
|
||||
refs[i].removeClass("hidden");
|
||||
}
|
||||
}
|
||||
|
||||
function enterEditMode() {
|
||||
|
@ -92,10 +112,8 @@ function enterEditMode() {
|
|||
editModeSelect($(this).attr("n"));
|
||||
});
|
||||
disableSendBtn();
|
||||
hide(button_actback);
|
||||
hide(button_actmem);
|
||||
hide(button_actretry);
|
||||
show(button_delete);
|
||||
hide([button_actback, button_actmem, button_actretry]);
|
||||
show([button_delete]);
|
||||
}
|
||||
|
||||
function exitEditMode() {
|
||||
|
@ -105,10 +123,8 @@ function exitEditMode() {
|
|||
game_text.children('chunk').removeClass("chunkhov");
|
||||
game_text.off('click', '> *');
|
||||
enableSendBtn();
|
||||
show(button_actback);
|
||||
show(button_actmem);
|
||||
show(button_actretry);
|
||||
hide(button_delete);
|
||||
show([button_actback, button_actmem, button_actretry]);
|
||||
hide([button_delete]);
|
||||
input_text.val("");
|
||||
}
|
||||
|
||||
|
@ -117,23 +133,20 @@ function editModeSelect(n) {
|
|||
}
|
||||
|
||||
function enterMemoryMode() {
|
||||
// Add class to each story chunk
|
||||
showMessage("Edit the memory to be sent with each request to the AI.");
|
||||
button_actmem.html("Cancel");
|
||||
hide(button_actback);
|
||||
hide(button_actretry);
|
||||
hide(button_actedit);
|
||||
hide(button_delete);
|
||||
hide([button_actback, button_actretry, button_actedit, button_delete]);
|
||||
// Display Author's Note field
|
||||
anote_menu.slideDown("fast");
|
||||
}
|
||||
|
||||
function exitMemoryMode() {
|
||||
// Remove class to each story chunk
|
||||
hideMessage();
|
||||
button_actmem.html("Memory");
|
||||
show(button_actback);
|
||||
show(button_actretry);
|
||||
show(button_actedit);
|
||||
show([button_actback, button_actretry, button_actedit]);
|
||||
input_text.val("");
|
||||
// Hide Author's Note field
|
||||
anote_menu.slideUp("fast");
|
||||
}
|
||||
|
||||
function dosubmit() {
|
||||
|
@ -160,7 +173,7 @@ function newTextHighlight(ref) {
|
|||
|
||||
$(document).ready(function(){
|
||||
|
||||
// Bind references
|
||||
// Bind UI references
|
||||
button_newgame = $('#btn_newgame');
|
||||
button_save = $('#btn_save');
|
||||
button_load = $('#btn_load');
|
||||
|
@ -182,6 +195,10 @@ $(document).ready(function(){
|
|||
label_topp = $('#settoppcur');
|
||||
label_reppen = $('#setreppencur');
|
||||
label_outlen = $('#setoutputcur');
|
||||
anote_menu = $('#anoterowcontainer');
|
||||
anote_input = $('#anoteinput');
|
||||
anote_labelcur = $('#anotecur');
|
||||
anote_slider = $('#anotedepth');
|
||||
|
||||
// Connect to SocketIO server
|
||||
socket = io.connect('http://127.0.0.1:5000');
|
||||
|
@ -203,24 +220,16 @@ $(document).ready(function(){
|
|||
// Enable or Disable buttons
|
||||
if(msg.data == "ready") {
|
||||
enableSendBtn();
|
||||
enableButton(button_actedit);
|
||||
enableButton(button_actmem);
|
||||
enableButton(button_actback);
|
||||
enableButton(button_actretry);
|
||||
enableButtons([button_actedit, button_actmem, button_actback, button_actretry]);
|
||||
hideWaitAnimation();
|
||||
} else if(msg.data == "wait") {
|
||||
disableSendBtn();
|
||||
disableButton(button_actedit);
|
||||
disableButton(button_actmem);
|
||||
disableButton(button_actback);
|
||||
disableButton(button_actretry);
|
||||
disableButtons([button_actedit, button_actmem, button_actback, button_actretry]);
|
||||
showWaitAnimation();
|
||||
} else if(msg.data == "start") {
|
||||
enableSendBtn();
|
||||
enableButton(button_actmem);
|
||||
disableButton(button_actedit);
|
||||
disableButton(button_actback);
|
||||
disableButton(button_actretry);
|
||||
enableButtons([button_actmem]);
|
||||
disableButtons([button_actedit, button_actback, button_actretry]);
|
||||
}
|
||||
} else if(msg.cmd == "editmode") {
|
||||
// Enable or Disable edit mode
|
||||
|
@ -276,6 +285,20 @@ $(document).ready(function(){
|
|||
} else if(msg.cmd == "setlabeloutput") {
|
||||
// Update setting label with value from server
|
||||
label_outlen.html(msg.data);
|
||||
} else if(msg.cmd == "updatanotedepth") {
|
||||
// Send current Author's Note depth value to input
|
||||
anote_slider.val(parseInt(msg.data));
|
||||
anote_labelcur.html(msg.data);
|
||||
} else if(msg.cmd == "setlabelanotedepth") {
|
||||
// Update setting label with value from server
|
||||
anote_labelcur.html(msg.data);
|
||||
} else if(msg.cmd == "getanote") {
|
||||
// Request contents of Author's Note field
|
||||
var txt = anote_input.val();
|
||||
socket.send({'cmd': 'anote', 'data': txt});
|
||||
} else if(msg.cmd == "setanote") {
|
||||
// Set contents of Author's Note field
|
||||
anote_input.val(msg.data);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -341,6 +364,7 @@ $(document).ready(function(){
|
|||
}
|
||||
});
|
||||
|
||||
// Enter to submit, but not if holding shift
|
||||
input_text.keyup(function (ev) {
|
||||
if (ev.which == 13 && do_clear_ent) {
|
||||
input_text.val("");
|
||||
|
|
|
@ -85,7 +85,7 @@ chunk {
|
|||
}
|
||||
|
||||
#input_text {
|
||||
height: 60px;
|
||||
height: 80px;
|
||||
resize: none;
|
||||
overflow:auto;
|
||||
background-color: #404040;
|
||||
|
@ -103,6 +103,38 @@ chunk {
|
|||
left:5px;
|
||||
}
|
||||
|
||||
#anoterowcontainer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#anoterow {
|
||||
margin-top: 10px;
|
||||
padding: 0px;
|
||||
width: 100%;
|
||||
display: grid;
|
||||
grid-template-columns: 90% 10%;
|
||||
}
|
||||
|
||||
#anoterowleft {
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
#anoteinput {
|
||||
background-color: #404040;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
#anoteslidelabel {
|
||||
color: #ffffff;
|
||||
display: grid;
|
||||
grid-template-columns: 80% 20%;
|
||||
}
|
||||
|
||||
.anotelabel {
|
||||
font-size: 10pt;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.airange {
|
||||
width: 100px;
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"rep_pen": 1.2, "temp": 1.0, "gamestarted": true, "prompt": "Niko the kobold stalked carefully down the alley, his small scaly figure obscured by a dusky cloak that fluttered lightly in the cold winter breeze. Holding up his tail to keep it from dragging in the dirty snow that covered the cobblestone, he waited patiently for the butcher to turn his attention from his stall so that he could pilfer his next meal: a tender-looking", "memory": "Niko is a small red kobold.\nNiko has yellow, reptilian eyes and a long, scaly tail.\nNiko is hungry and looking to steal something to eat.", "actions": [" chicken. He crouched just slightly as he neared the stall to ensure that no one was watching, not that anyone would be dumb enough to hassle a small kobold. What else was there for a lowly kobold to", " do in a city? All that Niko needed to know was", " where to find the chicken and then how to make off with it.\n\nA soft thud caused Niko to quickly lift his head. Standing behind the stall where the butcher had been cutting his chicken,"], "savedir": ""}
|
||||
{"gamestarted": true, "prompt": "Niko the kobold stalked carefully down the alley, his small scaly figure obscured by a dusky cloak that fluttered lightly in the cold winter breeze. Holding up his tail to keep it from dragging in the dirty snow that covered the cobblestone, he waited patiently for the butcher to turn his attention from his stall so that he could pilfer his next meal: a tender-looking", "memory": "Niko is a small red kobold.\nNiko has yellow, reptilian eyes and a long, scaly tail.\nNiko is hungry and looking to steal something to eat.", "authorsnote": "Things are about to get crazy for poor Niko.", "actions": [" chicken. He crouched just slightly as he neared the stall to ensure that no one was watching, not that anyone would be dumb enough to hassle a small kobold. What else was there for a lowly kobold to", " do in a city? All that Niko needed to know was", " where to find the chicken and then how to make off with it.\n\nA soft thud caused Niko to quickly lift his head. Standing behind the stall where the butcher had been cutting his chicken,"], "savedir": ""}
|
|
@ -102,7 +102,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<input type="range" class="form-range airange" min="10" max="500" step="1" id="setoutput">
|
||||
<input type="range" class="form-range airange" min="10" max="500" step="2" id="setoutput">
|
||||
</div>
|
||||
<div class="settingminmax">
|
||||
<div class="justifyleft">
|
||||
|
@ -138,6 +138,39 @@
|
|||
<button type="button" class="btn btn-secondary" id="btnsend">Wait</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="anoterowcontainer">
|
||||
<div id="anoterow">
|
||||
<div id="anoterowleft">
|
||||
<div class="anotelabel">
|
||||
Author's Note
|
||||
</div>
|
||||
<div class="anotefield">
|
||||
<input class="form-control" type="text" placeholder="Author's Note" id="anoteinput">
|
||||
</div>
|
||||
</div>
|
||||
<div id="anoterowright">
|
||||
<div id="anoteslidelabel">
|
||||
<div class="justifyleft">
|
||||
Depth
|
||||
</div>
|
||||
<div class="justifyright" id="anotecur">
|
||||
3
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<input type="range" class="form-range airange" min="1" max="5" step="1" id="anotedepth">
|
||||
</div>
|
||||
<div class="settingminmax">
|
||||
<div class="justifyleft">
|
||||
1
|
||||
</div>
|
||||
<div class="justifyright">
|
||||
5
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
|
Loading…
Reference in New Issue