From bbe3a92ce4804f9bef5de7a235b746f9ede7a922 Mon Sep 17 00:00:00 2001 From: Marcus Llewellyn Date: Fri, 25 Jun 2021 17:02:19 -0500 Subject: [PATCH 01/11] Fixes for unusual story loading circumstances. This PR does three things when loading a story from within the browser: 1. Prevents an error if a story file is not valid JSON. 2. Catches an error is a file is JSON, but lacks an actions property. 3. Replaces getcwd() and instead uses the path of the script file itself in case someone does not start the app from the current working directory. --- fileops.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/fileops.py b/fileops.py index dc5cb66e..640d3083 100644 --- a/fileops.py +++ b/fileops.py @@ -59,14 +59,22 @@ def getdirpath(dir, title): #==================================================================# def getstoryfiles(): list = [] - for file in listdir(getcwd()+"/stories"): + for file in listdir(path.dirname(path.realpath(__file__))+"/stories"): if file.endswith(".json"): ob = {} ob["name"] = file.replace(".json", "") - f = open(getcwd()+"/stories/"+file, "r") - js = json.load(f) + f = open(path.dirname(path.realpath(__file__))+"/stories/"+file, "r") + try: + js = json.load(f) + except: + print("Browser loading error: Story file is malformed or not a JSON file.") + f.close() + continue f.close() - ob["actions"] = len(js["actions"]) + try: + ob["actions"] = len(js["actions"]) + except TypeError: + print("Browser loading error: Story file has incorrect format.") list.append(ob) return list @@ -74,4 +82,4 @@ def getstoryfiles(): # Returns True if json file exists with requested save name #==================================================================# def saveexists(name): - return path.exists(getcwd()+"/stories/"+name+".json") \ No newline at end of file + return path.exists(path.dirname(os.path.realpath(__file__))+"/stories/"+name+".json") \ No newline at end of file From f9db17025db3e4901fbab2db9d2cc3102b46cfb2 Mon Sep 17 00:00:00 2001 From: Marcus Llewellyn Date: Fri, 25 Jun 2021 17:17:07 -0500 Subject: [PATCH 02/11] Ack! Forgot a continue statement. --- fileops.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fileops.py b/fileops.py index 640d3083..b4c02329 100644 --- a/fileops.py +++ b/fileops.py @@ -75,6 +75,7 @@ def getstoryfiles(): ob["actions"] = len(js["actions"]) except TypeError: print("Browser loading error: Story file has incorrect format.") + continue list.append(ob) return list @@ -82,4 +83,4 @@ def getstoryfiles(): # Returns True if json file exists with requested save name #==================================================================# def saveexists(name): - return path.exists(path.dirname(os.path.realpath(__file__))+"/stories/"+name+".json") \ No newline at end of file + return path.exists(path.dirname(os.path.realpath(__file__))+"/stories/"+name+".json") From ad39a4c8b3a8c010d1b1a8a25d7fd13cf1193366 Mon Sep 17 00:00:00 2001 From: Marcus Llewellyn Date: Fri, 25 Jun 2021 17:18:37 -0500 Subject: [PATCH 03/11] Ack! Forgot a continue statement. --- fileops.py | 1 + 1 file changed, 1 insertion(+) diff --git a/fileops.py b/fileops.py index 640d3083..9a574537 100644 --- a/fileops.py +++ b/fileops.py @@ -75,6 +75,7 @@ def getstoryfiles(): ob["actions"] = len(js["actions"]) except TypeError: print("Browser loading error: Story file has incorrect format.") + continue list.append(ob) return list From d5522f0d0ad6a6eb432bf2e7489f216d65c73ddd Mon Sep 17 00:00:00 2001 From: Marcus Llewellyn Date: Fri, 25 Jun 2021 17:31:43 -0500 Subject: [PATCH 04/11] Yet another silly mistake. Sigh. --- fileops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fileops.py b/fileops.py index b4c02329..70fd79b0 100644 --- a/fileops.py +++ b/fileops.py @@ -83,4 +83,4 @@ def getstoryfiles(): # Returns True if json file exists with requested save name #==================================================================# def saveexists(name): - return path.exists(path.dirname(os.path.realpath(__file__))+"/stories/"+name+".json") + return path.exists(path.dirname(path.realpath(__file__))+"/stories/"+name+".json") From 2a7c6244cb36317af896d7ef5f3f027616e75e5f Mon Sep 17 00:00:00 2001 From: Gnome Ann <> Date: Tue, 24 Aug 2021 13:45:20 -0400 Subject: [PATCH 05/11] Constant world info keys --- aiserver.py | 28 ++++++++++++---- static/application.js | 42 +++++++++++++++++++---- static/custom.css | 48 +++++++++++++++++++++++++++ static/open-iconic-bootstrap.min.css | 24 ++++++++++++++ static/open-iconic.woff | Bin 0 -> 14984 bytes templates/index.html | 1 + 6 files changed, 129 insertions(+), 14 deletions(-) create mode 100644 static/open-iconic-bootstrap.min.css create mode 100644 static/open-iconic.woff diff --git a/aiserver.py b/aiserver.py index ee7572e0..0e5a8f59 100644 --- a/aiserver.py +++ b/aiserver.py @@ -664,6 +664,10 @@ def get_message(msg): vars.worldinfo[msg['data']]["selective"] = True elif(msg['cmd'] == 'wiseloff'): vars.worldinfo[msg['data']]["selective"] = False + elif(msg['cmd'] == 'wiconstanton'): + vars.worldinfo[msg['data']]["constant"] = True + elif(msg['cmd'] == 'wiconstantoff'): + vars.worldinfo[msg['data']]["constant"] = False elif(msg['cmd'] == 'sendwilist'): commitwi(msg['data']) elif(msg['cmd'] == 'aidgimport'): @@ -1469,7 +1473,7 @@ def togglewimode(): # #==================================================================# def addwiitem(): - ob = {"key": "", "keysecondary": "", "content": "", "num": len(vars.worldinfo), "init": False, "selective": False} + ob = {"key": "", "keysecondary": "", "content": "", "num": len(vars.worldinfo), "init": False, "selective": False, "constant": False} vars.worldinfo.append(ob); emit('from_server', {'cmd': 'addwiitem', 'data': ob}, broadcast=True) @@ -1524,6 +1528,7 @@ def commitwi(ar): vars.worldinfo[ob["num"]]["keysecondary"] = ob["keysecondary"] vars.worldinfo[ob["num"]]["content"] = ob["content"] vars.worldinfo[ob["num"]]["selective"] = ob["selective"] + vars.worldinfo[ob["num"]]["constant"] = ob.get("constant", False) # Was this a deletion request? If so, remove the requested index if(vars.deletewi >= 0): del vars.worldinfo[vars.deletewi] @@ -1573,6 +1578,10 @@ def checkworldinfo(txt): # Scan text for matches on WI keys wimem = "" for wi in vars.worldinfo: + if(wi.get("constant", False)): + wimem = wimem + wi["content"] + "\n" + continue + if(wi["key"] != ""): # Split comma-separated keys keys = wi["key"].split(",") @@ -1792,12 +1801,13 @@ def saveRequest(savpath): # Extract only the important bits of WI for wi in vars.worldinfo: - if(wi["key"] != ""): + if(wi["constant"] or wi["key"] != ""): js["worldinfo"].append({ "key": wi["key"], "keysecondary": wi["keysecondary"], "content": wi["content"], - "selective": wi["selective"] + "selective": wi["selective"], + "constant": wi["constant"] }) # Write it @@ -1858,7 +1868,8 @@ def loadRequest(loadpath): "content": wi["content"], "num": num, "init": True, - "selective": wi.get("selective", False) + "selective": wi.get("selective", False), + "constant": wi.get("constant", False) }) num += 1 @@ -1976,7 +1987,8 @@ def importgame(): "content": wi["entry"], "num": num, "init": True, - "selective": wi.get("selective", False) + "selective": wi.get("selective", False), + "constant": wi.get("constant", False) }) num += 1 @@ -2022,7 +2034,8 @@ def importAidgRequest(id): "content": wi["entry"], "num": num, "init": True, - "selective": wi.get("selective", False) + "selective": wi.get("selective", False), + "constant": wi.get("constant", False) }) num += 1 @@ -2055,7 +2068,8 @@ def wiimportrequest(): "content": wi["entry"], "num": num, "init": True, - "selective": wi.get("selective", False) + "selective": wi.get("selective", False), + "constant": wi.get("constant", False) }) num += 1 diff --git a/static/application.js b/static/application.js index 6626f24f..7282e42c 100644 --- a/static/application.js +++ b/static/application.js @@ -189,13 +189,14 @@ function addWiLine(ob) { \ \ \ -
\ +
\ \ \ \ + \
\
\ - \ + \
\
\ \ @@ -209,10 +210,11 @@ function addWiLine(ob) { \ \
\ -
\ +
\ \ \ \ + \
\
\ \ @@ -239,13 +241,14 @@ function addWiLine(ob) { \ \
\ -
\ +
\ \ \ \ + \
\
\ - \ + \
\
\ \ @@ -258,6 +261,18 @@ function addWiLine(ob) { }); } // Assign actions to other elements + wientry_onfocus = function () { + $("#constant-key-"+ob.num).addClass("constant-key-icon-clickthrough"); + } + wientry_onfocusout = function () { + $("#constant-key-"+ob.num).removeClass("constant-key-icon-clickthrough"); + } + $("#wikey"+ob.num).on("focus", wientry_onfocus); + $("#wikeyprimary"+ob.num).on("focus", wientry_onfocus); + $("#wikeysecondary"+ob.num).on("focus", wientry_onfocus); + $("#wikey"+ob.num).on("focusout", wientry_onfocusout); + $("#wikeyprimary"+ob.num).on("focusout", wientry_onfocusout); + $("#wikeysecondary"+ob.num).on("focusout", wientry_onfocusout); $("#btn_wican"+ob.num).on("click", function () { hideWiDeleteConfirm(ob.num); }); @@ -270,10 +285,20 @@ function addWiLine(ob) { $("#btn_wiseloff"+ob.num).on("click", function () { disableWiSelective(ob.num); }); + $("#constant-key-"+ob.num).on("click", function () { + element = $("#constant-key-"+ob.num); + if(element.hasClass("constant-key-icon-enabled")) { + socket.send({'cmd': 'wiconstantoff', 'data': ob.num}); + element.removeClass("constant-key-icon-enabled") + } else { + socket.send({'cmd': 'wiconstanton', 'data': ob.num}); + element.addClass("constant-key-icon-enabled"); + } + }); } function expandWiLine(num) { - show([$("#wikey"+num), $("#wientry"+num), $("#btn_wiselon"+num)]); + show([$("#wikey"+num), $("#wientry"+num), $("#constant-key-"+num), $("#btn_wiselon"+num)]); $("#btn_wi"+num).html("X"); $("#btn_wi"+num).off(); // Tell server the WI entry was initialized @@ -297,6 +322,7 @@ function enableWiSelective(num) { hide([$("#btn_wiselon"+num), $("#wikey"+num)]); // Tell server the WI entry is now selective socket.send({'cmd': 'wiselon', 'data': num}); + $("#wikeyprimary"+num).val($("#wikey"+num).val()); show([$("#wikeyprimary"+num), $("#wikeysecondary"+num), $("#btn_wiseloff"+num)]); } @@ -304,6 +330,7 @@ function disableWiSelective(num) { hide([$("#btn_wiseloff"+num), $("#wikeyprimary"+num), $("#wikeysecondary"+num)]); // Tell server the WI entry is now non-selective socket.send({'cmd': 'wiseloff', 'data': num}); + $("#wikey"+num).val($("#wikeyprimary"+num).val()); show([$("#btn_wiselon"+num), $("#wikey"+num)]); } @@ -436,11 +463,12 @@ function returnWiList(ar) { var list = []; var i; for(i=0; i .constant-key-icon { + opacity: 40%; +} + +.constant-key-icon:hover { + opacity: 65%; + cursor: pointer; +} + +.constant-key-icon-enabled { + color: #3bf723; + opacity: 65% +} + +*:hover > .constant-key-icon-enabled { + opacity: 65%; +} + +.constant-key-icon-enabled:hover { + opacity: 100% +} + +.constant-key-icon-clickthrough { + opacity: 0% !important; + pointer-events: none; +} + +.constant-key-icon-clickthrough.constant-key-icon-enabled { + opacity: 35% !important; +} + .loadlistheader { padding-left: 10px; display: grid; diff --git a/static/open-iconic-bootstrap.min.css b/static/open-iconic-bootstrap.min.css new file mode 100644 index 00000000..a7012e57 --- /dev/null +++ b/static/open-iconic-bootstrap.min.css @@ -0,0 +1,24 @@ +/*! + * The MIT License (MIT) + * + * Copyright (c) 2014 Waybury + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +@font-face{font-family:Icons;src:url(open-iconic.eot);src:url(open-iconic.eot?#iconic-sm) format('embedded-opentype'),url(open-iconic.woff) format('woff'),url(open-iconic.ttf) format('truetype'),url(open-iconic.otf) format('opentype'),url(open-iconic.svg#iconic-sm) format('svg');font-weight:400;font-style:normal}.oi{position:relative;top:1px;display:inline-block;speak:none;font-family:Icons;font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.oi:empty:before{width:1em;text-align:center;box-sizing:content-box}.oi.oi-align-center:before{text-align:center}.oi.oi-align-left:before{text-align:left}.oi.oi-align-right:before{text-align:right}.oi.oi-flip-horizontal:before{-webkit-transform:scale(-1,1);-ms-transform:scale(-1,1);transform:scale(-1,1)}.oi.oi-flip-vertical:before{-webkit-transform:scale(1,-1);-ms-transform:scale(-1,1);transform:scale(1,-1)}.oi.oi-flip-horizontal-vertical:before{-webkit-transform:scale(-1,-1);-ms-transform:scale(-1,1);transform:scale(-1,-1)}.oi-account-login:before{content:'\e000'}.oi-account-logout:before{content:'\e001'}.oi-action-redo:before{content:'\e002'}.oi-action-undo:before{content:'\e003'}.oi-align-center:before{content:'\e004'}.oi-align-left:before{content:'\e005'}.oi-align-right:before{content:'\e006'}.oi-aperture:before{content:'\e007'}.oi-arrow-bottom:before{content:'\e008'}.oi-arrow-circle-bottom:before{content:'\e009'}.oi-arrow-circle-left:before{content:'\e00a'}.oi-arrow-circle-right:before{content:'\e00b'}.oi-arrow-circle-top:before{content:'\e00c'}.oi-arrow-left:before{content:'\e00d'}.oi-arrow-right:before{content:'\e00e'}.oi-arrow-thick-bottom:before{content:'\e00f'}.oi-arrow-thick-left:before{content:'\e010'}.oi-arrow-thick-right:before{content:'\e011'}.oi-arrow-thick-top:before{content:'\e012'}.oi-arrow-top:before{content:'\e013'}.oi-audio-spectrum:before{content:'\e014'}.oi-audio:before{content:'\e015'}.oi-badge:before{content:'\e016'}.oi-ban:before{content:'\e017'}.oi-bar-chart:before{content:'\e018'}.oi-basket:before{content:'\e019'}.oi-battery-empty:before{content:'\e01a'}.oi-battery-full:before{content:'\e01b'}.oi-beaker:before{content:'\e01c'}.oi-bell:before{content:'\e01d'}.oi-bluetooth:before{content:'\e01e'}.oi-bold:before{content:'\e01f'}.oi-bolt:before{content:'\e020'}.oi-book:before{content:'\e021'}.oi-bookmark:before{content:'\e022'}.oi-box:before{content:'\e023'}.oi-briefcase:before{content:'\e024'}.oi-british-pound:before{content:'\e025'}.oi-browser:before{content:'\e026'}.oi-brush:before{content:'\e027'}.oi-bug:before{content:'\e028'}.oi-bullhorn:before{content:'\e029'}.oi-calculator:before{content:'\e02a'}.oi-calendar:before{content:'\e02b'}.oi-camera-slr:before{content:'\e02c'}.oi-caret-bottom:before{content:'\e02d'}.oi-caret-left:before{content:'\e02e'}.oi-caret-right:before{content:'\e02f'}.oi-caret-top:before{content:'\e030'}.oi-cart:before{content:'\e031'}.oi-chat:before{content:'\e032'}.oi-check:before{content:'\e033'}.oi-chevron-bottom:before{content:'\e034'}.oi-chevron-left:before{content:'\e035'}.oi-chevron-right:before{content:'\e036'}.oi-chevron-top:before{content:'\e037'}.oi-circle-check:before{content:'\e038'}.oi-circle-x:before{content:'\e039'}.oi-clipboard:before{content:'\e03a'}.oi-clock:before{content:'\e03b'}.oi-cloud-download:before{content:'\e03c'}.oi-cloud-upload:before{content:'\e03d'}.oi-cloud:before{content:'\e03e'}.oi-cloudy:before{content:'\e03f'}.oi-code:before{content:'\e040'}.oi-cog:before{content:'\e041'}.oi-collapse-down:before{content:'\e042'}.oi-collapse-left:before{content:'\e043'}.oi-collapse-right:before{content:'\e044'}.oi-collapse-up:before{content:'\e045'}.oi-command:before{content:'\e046'}.oi-comment-square:before{content:'\e047'}.oi-compass:before{content:'\e048'}.oi-contrast:before{content:'\e049'}.oi-copywriting:before{content:'\e04a'}.oi-credit-card:before{content:'\e04b'}.oi-crop:before{content:'\e04c'}.oi-dashboard:before{content:'\e04d'}.oi-data-transfer-download:before{content:'\e04e'}.oi-data-transfer-upload:before{content:'\e04f'}.oi-delete:before{content:'\e050'}.oi-dial:before{content:'\e051'}.oi-document:before{content:'\e052'}.oi-dollar:before{content:'\e053'}.oi-double-quote-sans-left:before{content:'\e054'}.oi-double-quote-sans-right:before{content:'\e055'}.oi-double-quote-serif-left:before{content:'\e056'}.oi-double-quote-serif-right:before{content:'\e057'}.oi-droplet:before{content:'\e058'}.oi-eject:before{content:'\e059'}.oi-elevator:before{content:'\e05a'}.oi-ellipses:before{content:'\e05b'}.oi-envelope-closed:before{content:'\e05c'}.oi-envelope-open:before{content:'\e05d'}.oi-euro:before{content:'\e05e'}.oi-excerpt:before{content:'\e05f'}.oi-expand-down:before{content:'\e060'}.oi-expand-left:before{content:'\e061'}.oi-expand-right:before{content:'\e062'}.oi-expand-up:before{content:'\e063'}.oi-external-link:before{content:'\e064'}.oi-eye:before{content:'\e065'}.oi-eyedropper:before{content:'\e066'}.oi-file:before{content:'\e067'}.oi-fire:before{content:'\e068'}.oi-flag:before{content:'\e069'}.oi-flash:before{content:'\e06a'}.oi-folder:before{content:'\e06b'}.oi-fork:before{content:'\e06c'}.oi-fullscreen-enter:before{content:'\e06d'}.oi-fullscreen-exit:before{content:'\e06e'}.oi-globe:before{content:'\e06f'}.oi-graph:before{content:'\e070'}.oi-grid-four-up:before{content:'\e071'}.oi-grid-three-up:before{content:'\e072'}.oi-grid-two-up:before{content:'\e073'}.oi-hard-drive:before{content:'\e074'}.oi-header:before{content:'\e075'}.oi-headphones:before{content:'\e076'}.oi-heart:before{content:'\e077'}.oi-home:before{content:'\e078'}.oi-image:before{content:'\e079'}.oi-inbox:before{content:'\e07a'}.oi-infinity:before{content:'\e07b'}.oi-info:before{content:'\e07c'}.oi-italic:before{content:'\e07d'}.oi-justify-center:before{content:'\e07e'}.oi-justify-left:before{content:'\e07f'}.oi-justify-right:before{content:'\e080'}.oi-key:before{content:'\e081'}.oi-laptop:before{content:'\e082'}.oi-layers:before{content:'\e083'}.oi-lightbulb:before{content:'\e084'}.oi-link-broken:before{content:'\e085'}.oi-link-intact:before{content:'\e086'}.oi-list-rich:before{content:'\e087'}.oi-list:before{content:'\e088'}.oi-location:before{content:'\e089'}.oi-lock-locked:before{content:'\e08a'}.oi-lock-unlocked:before{content:'\e08b'}.oi-loop-circular:before{content:'\e08c'}.oi-loop-square:before{content:'\e08d'}.oi-loop:before{content:'\e08e'}.oi-magnifying-glass:before{content:'\e08f'}.oi-map-marker:before{content:'\e090'}.oi-map:before{content:'\e091'}.oi-media-pause:before{content:'\e092'}.oi-media-play:before{content:'\e093'}.oi-media-record:before{content:'\e094'}.oi-media-skip-backward:before{content:'\e095'}.oi-media-skip-forward:before{content:'\e096'}.oi-media-step-backward:before{content:'\e097'}.oi-media-step-forward:before{content:'\e098'}.oi-media-stop:before{content:'\e099'}.oi-medical-cross:before{content:'\e09a'}.oi-menu:before{content:'\e09b'}.oi-microphone:before{content:'\e09c'}.oi-minus:before{content:'\e09d'}.oi-monitor:before{content:'\e09e'}.oi-moon:before{content:'\e09f'}.oi-move:before{content:'\e0a0'}.oi-musical-note:before{content:'\e0a1'}.oi-paperclip:before{content:'\e0a2'}.oi-pencil:before{content:'\e0a3'}.oi-people:before{content:'\e0a4'}.oi-person:before{content:'\e0a5'}.oi-phone:before{content:'\e0a6'}.oi-pie-chart:before{content:'\e0a7'}.oi-pin:before{content:'\e0a8'}.oi-play-circle:before{content:'\e0a9'}.oi-plus:before{content:'\e0aa'}.oi-power-standby:before{content:'\e0ab'}.oi-print:before{content:'\e0ac'}.oi-project:before{content:'\e0ad'}.oi-pulse:before{content:'\e0ae'}.oi-puzzle-piece:before{content:'\e0af'}.oi-question-mark:before{content:'\e0b0'}.oi-rain:before{content:'\e0b1'}.oi-random:before{content:'\e0b2'}.oi-reload:before{content:'\e0b3'}.oi-resize-both:before{content:'\e0b4'}.oi-resize-height:before{content:'\e0b5'}.oi-resize-width:before{content:'\e0b6'}.oi-rss-alt:before{content:'\e0b7'}.oi-rss:before{content:'\e0b8'}.oi-script:before{content:'\e0b9'}.oi-share-boxed:before{content:'\e0ba'}.oi-share:before{content:'\e0bb'}.oi-shield:before{content:'\e0bc'}.oi-signal:before{content:'\e0bd'}.oi-signpost:before{content:'\e0be'}.oi-sort-ascending:before{content:'\e0bf'}.oi-sort-descending:before{content:'\e0c0'}.oi-spreadsheet:before{content:'\e0c1'}.oi-star:before{content:'\e0c2'}.oi-sun:before{content:'\e0c3'}.oi-tablet:before{content:'\e0c4'}.oi-tag:before{content:'\e0c5'}.oi-tags:before{content:'\e0c6'}.oi-target:before{content:'\e0c7'}.oi-task:before{content:'\e0c8'}.oi-terminal:before{content:'\e0c9'}.oi-text:before{content:'\e0ca'}.oi-thumb-down:before{content:'\e0cb'}.oi-thumb-up:before{content:'\e0cc'}.oi-timer:before{content:'\e0cd'}.oi-transfer:before{content:'\e0ce'}.oi-trash:before{content:'\e0cf'}.oi-underline:before{content:'\e0d0'}.oi-vertical-align-bottom:before{content:'\e0d1'}.oi-vertical-align-center:before{content:'\e0d2'}.oi-vertical-align-top:before{content:'\e0d3'}.oi-video:before{content:'\e0d4'}.oi-volume-high:before{content:'\e0d5'}.oi-volume-low:before{content:'\e0d6'}.oi-volume-off:before{content:'\e0d7'}.oi-warning:before{content:'\e0d8'}.oi-wifi:before{content:'\e0d9'}.oi-wrench:before{content:'\e0da'}.oi-x:before{content:'\e0db'}.oi-yen:before{content:'\e0dc'}.oi-zoom-in:before{content:'\e0dd'}.oi-zoom-out:before{content:'\e0de'} \ No newline at end of file diff --git a/static/open-iconic.woff b/static/open-iconic.woff new file mode 100644 index 0000000000000000000000000000000000000000..f9309988aeab3868040d3b322658902098eba27f GIT binary patch literal 14984 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 literal 0 HcmV?d00001 diff --git a/templates/index.html b/templates/index.html index 97eee72d..6f4a4248 100644 --- a/templates/index.html +++ b/templates/index.html @@ -13,6 +13,7 @@ +
From cbdf3fa037bb43b7871b84e6da225c8bea774fed Mon Sep 17 00:00:00 2001 From: Gnome Ann <> Date: Tue, 24 Aug 2021 19:29:40 -0400 Subject: [PATCH 06/11] Display names of problematic story JSON files --- fileops.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fileops.py b/fileops.py index 70fd79b0..7ae96836 100644 --- a/fileops.py +++ b/fileops.py @@ -67,14 +67,14 @@ def getstoryfiles(): try: js = json.load(f) except: - print("Browser loading error: Story file is malformed or not a JSON file.") + print(f"Browser loading error: {file} is malformed or not a JSON file.") f.close() continue f.close() try: ob["actions"] = len(js["actions"]) except TypeError: - print("Browser loading error: Story file has incorrect format.") + print(f"Browser loading error: {file} has incorrect format.") continue list.append(ob) return list From 3da0c3d24a745660ea8b089ba5a8c2eaabce1d6f Mon Sep 17 00:00:00 2001 From: henk717 Date: Wed, 25 Aug 2021 13:57:38 +0200 Subject: [PATCH 07/11] Remote improvements Some colab's use KoboldAI as a subprocess, rather than making that to complicated for Colab developers its better to just dump the Cloudflare link to a log, in addition to showing the message on screen. That way if KoboldAI itself gets filtered you can easily cat the link or use the existing link grabbing methods. --- aiserver.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/aiserver.py b/aiserver.py index ee7572e0..1a03d7c6 100644 --- a/aiserver.py +++ b/aiserver.py @@ -2104,13 +2104,17 @@ if __name__ == "__main__": loadsettings() # Start Flask/SocketIO (Blocking, so this must be last method!) - print("{0}Server started!\rYou may now connect with a browser at http://127.0.0.1:5000/{1}".format(colors.GREEN, colors.END)) + #socketio.run(app, host='0.0.0.0', port=5000) if(vars.remote): - from flask_cloudflared import start_cloudflared - start_cloudflared(5000) + from flask_cloudflared import _run_cloudflared + cloudflare = _run_cloudflared(5000) + with open('cloudflare.log', 'w') as cloudflarelog: + cloudflarelog.write("KoboldAI has finished loading and is available in the following link : " + cloudflare) + print(format(colors.GREEN) + "KoboldAI has finished loading and is available in the following link : " + cloudflare + format(colors.END)) socketio.run(app, host='0.0.0.0', port=5000) else: import webbrowser webbrowser.open_new('http://localhost:5000') + print("{0}Server started!\rYou may now connect with a browser at http://127.0.0.1:5000/{1}".format(colors.GREEN, colors.END)) socketio.run(app) From 9d2e21de1b72b2d65dedf68ef0c1c7ebe8688703 Mon Sep 17 00:00:00 2001 From: henk717 Date: Wed, 25 Aug 2021 14:30:37 +0200 Subject: [PATCH 08/11] Updated requirements.txt ColabKobold already had this, we need the requirements.txt updated as well since Colab does not use Conda. --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index a17ea08f..40f66af4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -transformers == 4.5.1 +git+https://github.com/finetuneanon/transformers@gpt-neo-localattention3-rp-b tensorflow-gpu Flask == 1.1.2 Flask-SocketIO == 5.0.1 From cf677c60fc2b0722104ee26801230a5a3270248f Mon Sep 17 00:00:00 2001 From: Gnome Ann <> Date: Wed, 25 Aug 2021 14:42:37 -0400 Subject: [PATCH 09/11] Stability fixes for back/retry re genseqs/useprompt * Back and Retry buttons no longer pop a story chunk while in the "Select sequence to keep" menu * No longer freezes if you retry with no story chunks beyond the initial prompt chunk * When "Always Add Prompt" is on, allow Retry even if the prompt is the only chunk in the story * Added error messages for Back and Retry buttons --- aiserver.py | 16 ++++++++++++---- static/application.js | 2 ++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/aiserver.py b/aiserver.py index f34734ae..7efd06a0 100644 --- a/aiserver.py +++ b/aiserver.py @@ -878,12 +878,16 @@ def actionretry(data): return if(vars.aibusy): return - set_aibusy(1) # Remove last action if possible and resubmit - if(len(vars.actions) > 0): - vars.actions.pop() + if(vars.gamestarted if vars.useprompt else len(vars.actions) > 0): + set_aibusy(1) + if(len(vars.actions) != 0 and len(vars.genseqs) == 0): # Don't pop if we're in the "Select sequence to keep" menu or if there are no non-prompt actions + vars.actions.pop() + vars.genseqs = [] refresh_story() calcsubmit('') + elif(not vars.useprompt): + emit('from_server', {'cmd': 'errmsg', 'data': "Please enable \"Always Add Prompt\" to retry with your prompt."}) #==================================================================# # @@ -892,9 +896,13 @@ def actionback(): if(vars.aibusy): return # Remove last index of actions and refresh game screen - if(len(vars.actions) > 0): + if(len(vars.genseqs) == 0 and len(vars.actions) > 0): vars.actions.pop() refresh_story() + elif(len(vars.genseqs) == 0): + emit('from_server', {'cmd': 'errmsg', 'data': "Cannot delete the prompt."}) + else: + vars.genseqs = [] #==================================================================# # Take submitted text and build the text to be given to generator diff --git a/static/application.js b/static/application.js index 9e663874..6828c9fa 100644 --- a/static/application.js +++ b/static/application.js @@ -1070,11 +1070,13 @@ $(document).ready(function(){ }); button_actretry.on("click", function(ev) { + hideMessage(); socket.send({'cmd': 'retry', 'data': ''}); hidegenseqs(); }); button_actback.on("click", function(ev) { + hideMessage(); socket.send({'cmd': 'back', 'data': ''}); hidegenseqs(); }); From c3528e6221cc64d0f3730cee32555652a25199d9 Mon Sep 17 00:00:00 2001 From: Gnome Ann <> Date: Wed, 25 Aug 2021 14:54:51 -0400 Subject: [PATCH 10/11] Retry after Back no longer pops an extra story chunk --- aiserver.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/aiserver.py b/aiserver.py index 7efd06a0..828f5612 100644 --- a/aiserver.py +++ b/aiserver.py @@ -102,6 +102,7 @@ class vars: svowname = "" # Filename that was flagged for overwrite confirm saveow = False # Whether or not overwrite confirm has been displayed genseqs = [] # Temporary storage for generated sequences + recentback = False # Whether Back button was recently used without Submitting or Retrying after 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 bmsupported = False # Whether the breakmodel option is supported (GPT-Neo/GPT-J only, currently) @@ -825,6 +826,7 @@ def actionsubmit(data, actionmode=0): return set_aibusy(1) + vars.recentback = False vars.actionmode = actionmode # "Action" mode @@ -881,11 +883,12 @@ def actionretry(data): # Remove last action if possible and resubmit if(vars.gamestarted if vars.useprompt else len(vars.actions) > 0): set_aibusy(1) - if(len(vars.actions) != 0 and len(vars.genseqs) == 0): # Don't pop if we're in the "Select sequence to keep" menu or if there are no non-prompt actions + if(not vars.recentback and len(vars.actions) != 0 and len(vars.genseqs) == 0): # Don't pop if we're in the "Select sequence to keep" menu or if there are no non-prompt actions vars.actions.pop() vars.genseqs = [] refresh_story() calcsubmit('') + vars.recentback = False elif(not vars.useprompt): emit('from_server', {'cmd': 'errmsg', 'data': "Please enable \"Always Add Prompt\" to retry with your prompt."}) @@ -898,6 +901,7 @@ def actionback(): # Remove last index of actions and refresh game screen if(len(vars.genseqs) == 0 and len(vars.actions) > 0): vars.actions.pop() + vars.recentback = True refresh_story() elif(len(vars.genseqs) == 0): emit('from_server', {'cmd': 'errmsg', 'data': "Cannot delete the prompt."}) From 6dcd7888c8b367dc5e77b01ff648e1cd4c82c446 Mon Sep 17 00:00:00 2001 From: Gnome Ann <> Date: Wed, 25 Aug 2021 14:55:26 -0400 Subject: [PATCH 11/11] Change "recieved" to "received" --- aiserver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aiserver.py b/aiserver.py index 828f5612..8b99c0fb 100644 --- a/aiserver.py +++ b/aiserver.py @@ -529,7 +529,7 @@ def do_connect(): #==================================================================# @socketio.on('message') def get_message(msg): - print("{0}Data recieved:{1}{2}".format(colors.GREEN, msg, colors.END)) + print("{0}Data received:{1}{2}".format(colors.GREEN, msg, colors.END)) # Submit action if(msg['cmd'] == 'submit'): if(vars.mode == "play"):