From 13b81c7523401dbe4e07b0c09fc2b9963b5fa5f5 Mon Sep 17 00:00:00 2001 From: Gnome Ann <> Date: Mon, 27 Sep 2021 22:21:14 -0400 Subject: [PATCH 1/9] Prevent the user from deleting the prompt --- aiserver.py | 2 ++ static/application.js | 43 +++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/aiserver.py b/aiserver.py index beb2aa4d..1a143ece 100644 --- a/aiserver.py +++ b/aiserver.py @@ -1578,6 +1578,8 @@ def deleterequest(): def inlineedit(chunk, data): chunk = int(chunk) if(chunk == 0): + if(len(data.strip()) == 0): + return vars.prompt = data else: vars.actions[chunk-1] = data diff --git a/static/application.js b/static/application.js index 89c8bf7d..7ac59323 100644 --- a/static/application.js +++ b/static/application.js @@ -73,6 +73,7 @@ var modified_chunks = new Set(); var empty_chunks = new Set(); var mutation_observer = null; var gametext_bound = false; +var saved_prompt = "..."; var sman_allow_delete = false; var sman_allow_rename = false; @@ -844,7 +845,8 @@ function applyChunkDeltas(nodes) { var selected_chunks = buildChunkSetFromNodeArray(getSelectedNodes()); for(var i = 0; i < chunks.length; i++) { var chunk = document.getElementById("n" + chunks[i]); - if(chunk && chunk.innerText.length != 0) { + if(chunk && chunk.innerText.length != 0 && chunks[i] != '0') { + console.log(chunks[i]) if(!selected_chunks.has(chunks[i])) { modified_chunks.delete(chunks[i]); socket.send({'cmd': 'inlineedit', 'chunk': chunks[i], 'data': chunk.innerText.replace(/\u00a0/g, " ")}); @@ -879,12 +881,46 @@ function syncAllModifiedChunks(including_selected_chunks=false) { } } +function restorePrompt() { + if($("#gametext > chunk").length == 0 && game_text[0].innerText.trim().length) { + saved_prompt = game_text[0].innerText.replace(/\u00a0/g, " "); + unbindGametext(); + game_text[0].innerText = ""; + bindGametext(); + } + if($("#n0").length) { + $("#n0").remove(); + } + var prompt_chunk = document.createElement("chunk"); + prompt_chunk.setAttribute("n", "0"); + prompt_chunk.setAttribute("id", "n0"); + prompt_chunk.setAttribute("tabindex", "-1"); + prompt_chunk.innerText = saved_prompt; + unbindGametext(); + game_text[0].prepend(prompt_chunk); + bindGametext(); + modified_chunks.delete('0'); + empty_chunks.delete('0'); + socket.send({'cmd': 'inlineedit', 'chunk': '0', 'data': saved_prompt}); +} + function deleteEmptyChunks() { var chunks = Array.from(empty_chunks); for(var i = 0; i < chunks.length; i++) { empty_chunks.delete(chunks[i]); - socket.send({'cmd': 'inlinedelete', 'data': chunks[i]}); + if(chunks[i] === "0") { + // Don't delete the prompt + restorePrompt(); + } else { + socket.send({'cmd': 'inlinedelete', 'data': chunks[i]}); + } } + if(modified_chunks.has('0')) { + modified_chunks.delete(chunks[i]); + socket.send({'cmd': 'inlineedit', 'chunk': chunks[i], 'data': document.getElementById("n0").innerText.replace(/\u00a0/g, " ")}); + } + console.log(empty_chunks) + saved_prompt = $("#n0")[0].innerText.replace(/\u00a0/g, " "); } function highlightEditingChunks() { @@ -1094,6 +1130,9 @@ $(document).ready(function(){ empty_chunks = new Set(); game_text.html(msg.data); bindGametext(); + if(gamestarted) { + saved_prompt = $("#n0")[0].innerText.replace(/\u00a0/g, " "); + } // Scroll to bottom of text if(newly_loaded) { scrollToBottom(); From 431acce0bb5ac1aa52e6f5bdd63d35804b08a8fd Mon Sep 17 00:00:00 2001 From: henk717 Date: Tue, 28 Sep 2021 04:44:17 +0200 Subject: [PATCH 2/9] Remove unneeded scrollbars There was an unneeded scrollbar growing as you were expanding towards the top of the screen, now this scrollbar is hidden until you actually need it making the UI look a bit more polished. --- static/custom.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/custom.css b/static/custom.css index 1f186696..fa9be0b4 100644 --- a/static/custom.css +++ b/static/custom.css @@ -86,7 +86,7 @@ chunk.editing, chunk.editing * { width: 100%; word-wrap: break-word; padding: 10px; - overflow-y: scroll; + overflow-y: auto; } #seqselmenu { From b39f12b60b2383d5c297a4cefd5160a97f5cff2c Mon Sep 17 00:00:00 2001 From: Gnome Ann <> Date: Mon, 27 Sep 2021 22:47:27 -0400 Subject: [PATCH 3/9] Remove unneeded console.log() --- static/application.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/static/application.js b/static/application.js index 7ac59323..2b345a1c 100644 --- a/static/application.js +++ b/static/application.js @@ -846,7 +846,6 @@ function applyChunkDeltas(nodes) { for(var i = 0; i < chunks.length; i++) { var chunk = document.getElementById("n" + chunks[i]); if(chunk && chunk.innerText.length != 0 && chunks[i] != '0') { - console.log(chunks[i]) if(!selected_chunks.has(chunks[i])) { modified_chunks.delete(chunks[i]); socket.send({'cmd': 'inlineedit', 'chunk': chunks[i], 'data': chunk.innerText.replace(/\u00a0/g, " ")}); @@ -919,7 +918,6 @@ function deleteEmptyChunks() { modified_chunks.delete(chunks[i]); socket.send({'cmd': 'inlineedit', 'chunk': chunks[i], 'data': document.getElementById("n0").innerText.replace(/\u00a0/g, " ")}); } - console.log(empty_chunks) saved_prompt = $("#n0")[0].innerText.replace(/\u00a0/g, " "); } From 3297c27fcdaba0b45659379c8254007ac62321c2 Mon Sep 17 00:00:00 2001 From: Gnome Ann <> Date: Mon, 27 Sep 2021 22:56:12 -0400 Subject: [PATCH 4/9] Fix using escape to save edits --- static/application.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/static/application.js b/static/application.js index 2b345a1c..7c64b116 100644 --- a/static/application.js +++ b/static/application.js @@ -74,6 +74,7 @@ var empty_chunks = new Set(); var mutation_observer = null; var gametext_bound = false; var saved_prompt = "..."; +var override_focusout = false; var sman_allow_delete = false; var sman_allow_rename = false; @@ -733,9 +734,8 @@ function chunkOnKeyDown(event) { // Make escape commit the changes (Originally we had Enter here to but its not required and nicer for users if we let them type freely // You can add the following after 27 if you want it back to committing on enter : || (!event.shiftKey && event.keyCode == 13) if(event.keyCode == 27) { - setTimeout(function () { - event.target.blur(); - }, 5); + override_focusout = true; + game_text.blur(); event.preventDefault(); return; } @@ -974,7 +974,8 @@ function chunkOnPaste(event) { // This gets run every time the caret moves in the editor function chunkOnSelectionChange(event) { - if(!gametext_bound) { + if(!gametext_bound || override_focusout) { + override_focusout = false; return; } setTimeout(function() { From e8cefcc2c65d8d290919333bf23ce737733fc572 Mon Sep 17 00:00:00 2001 From: Gnome Ann <> Date: Mon, 27 Sep 2021 23:10:34 -0400 Subject: [PATCH 5/9] (Firefox) Fix certain chunks not updating when editing I don't know why Firefox is so weird but we need to do this or else some chunks don't update properly when you edit multiple chunks at the same time. One example of when this happened is when you have a story with at least one chunk other than the prompt. Then, if you select the entire story except for the first few characters of the prompt and then delete the selected characters, and then defocus the story to save your changes, the last chunk in the story will not register as having been deleted, which you can verify if you refresh the page. Another example. If your story has a chunk with no trailing newlines followed by a chunk with exactly two leading newlines, if you delete the first newline from the latter chunk and then defocus to save your edits, the newline will be there again when you refresh the page. --- static/application.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/static/application.js b/static/application.js index 7c64b116..04a5a45b 100644 --- a/static/application.js +++ b/static/application.js @@ -950,11 +950,8 @@ function chunkOnDOMMutate(mutations, observer) { var nodes = []; for(var i = 0; i < mutations.length; i++) { var mutation = mutations[i]; - if(mutation.type === "childList") { - nodes = nodes.concat(Array.from(mutation.addedNodes), Array.from(mutation.removedNodes)); - } else { - nodes.push(mutation.target); - } + nodes = nodes.concat(Array.from(mutation.addedNodes), Array.from(mutation.removedNodes)); + nodes.push(mutation.target); } applyChunkDeltas(nodes); } From f632ad988083cdfbf23748f71e443dbce3e713be Mon Sep 17 00:00:00 2001 From: Gnome Ann <> Date: Mon, 27 Sep 2021 23:38:41 -0400 Subject: [PATCH 6/9] Use a more robust scheme for restorePrompt The original way didn't work properly in Firefox on Android when you select all text and replace it with something else. --- static/application.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/static/application.js b/static/application.js index 04a5a45b..b696e85c 100644 --- a/static/application.js +++ b/static/application.js @@ -881,8 +881,8 @@ function syncAllModifiedChunks(including_selected_chunks=false) { } function restorePrompt() { - if($("#gametext > chunk").length == 0 && game_text[0].innerText.trim().length) { - saved_prompt = game_text[0].innerText.replace(/\u00a0/g, " "); + if(game_text[0].firstChild.nodeType === 3) { + saved_prompt = game_text[0].firstChild.textContent.replace(/\u00a0/g, " "); unbindGametext(); game_text[0].innerText = ""; bindGametext(); From 231290608d102958428176aad3dd4dfbc6742bf7 Mon Sep 17 00:00:00 2001 From: Gnome Ann <> Date: Tue, 28 Sep 2021 00:48:37 -0400 Subject: [PATCH 7/9] Do a better job of preventing editing of text when required --- aiserver.py | 2 +- static/application.js | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/aiserver.py b/aiserver.py index 1a143ece..d37eb810 100644 --- a/aiserver.py +++ b/aiserver.py @@ -956,7 +956,7 @@ def actionsubmit(data, actionmode=0): vars.prompt = data if(not vars.noai): # Clear the startup text from game screen - emit('from_server', {'cmd': 'updatescreen', 'gamestarted': vars.gamestarted, 'data': 'Please wait, generating story...'}, broadcast=True) + emit('from_server', {'cmd': 'updatescreen', 'gamestarted': False, 'data': 'Please wait, generating story...'}, broadcast=True) calcsubmit(data) # Run the first action through the generator emit('from_server', {'cmd': 'scrolldown', 'data': ''}, broadcast=True) else: diff --git a/static/application.js b/static/application.js index b696e85c..f8692b7e 100644 --- a/static/application.js +++ b/static/application.js @@ -944,7 +944,7 @@ function highlightEditingChunks() { // This gets run every time the text in a chunk is edited // or a chunk is deleted function chunkOnDOMMutate(mutations, observer) { - if(!gametext_bound) { + if(!gametext_bound || !allowedit) { return; } var nodes = []; @@ -971,7 +971,7 @@ function chunkOnPaste(event) { // This gets run every time the caret moves in the editor function chunkOnSelectionChange(event) { - if(!gametext_bound || override_focusout) { + if(!gametext_bound || !allowedit || override_focusout) { override_focusout = false; return; } @@ -992,7 +992,7 @@ function chunkOnSelectionChange(event) { // This gets run when you defocus the editor by clicking // outside of the editor or by pressing escape or tab function chunkOnFocusOut(event) { - if(!gametext_bound || event.target !== game_text[0]) { + if(!gametext_bound || !allowedit || event.target !== game_text[0]) { return; } setTimeout(function() { @@ -1108,7 +1108,7 @@ $(document).ready(function(){ $('body').on('input', autofocus); $('#allowediting').prop('checked', allowedit).prop('disabled', false).change().off('change').on('change', function () { if(allowtoggle) { - allowedit = $(this).prop('checked'); + allowedit = gamestarted && $(this).prop('checked'); game_text.attr('contenteditable', allowedit); } }); From 97e1760af5d38ae191d0b53810424983d24eb5ed Mon Sep 17 00:00:00 2001 From: Gnome Ann <> Date: Tue, 28 Sep 2021 01:07:11 -0400 Subject: [PATCH 8/9] Prevent retry from popping chunks after edit/delete --- aiserver.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/aiserver.py b/aiserver.py index d37eb810..7ee18541 100644 --- a/aiserver.py +++ b/aiserver.py @@ -937,6 +937,7 @@ def actionsubmit(data, actionmode=0): set_aibusy(1) vars.recentback = False + vars.recentedit = False vars.actionmode = actionmode # "Action" mode @@ -993,13 +994,14 @@ 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(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 + if(not vars.recentback and not vars.recentedit 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 last_key = vars.actions.get_last_key() vars.actions.pop() remove_story_chunk(last_key + 1) vars.genseqs = [] calcsubmit('') vars.recentback = False + vars.recentedit = False elif(not vars.useprompt): emit('from_server', {'cmd': 'errmsg', 'data': "Please enable \"Always Add Prompt\" to retry with your prompt."}) @@ -1548,6 +1550,7 @@ def editrequest(n): # #==================================================================# def editsubmit(data): + vars.recentedit = True if(vars.editln == 0): vars.prompt = data else: @@ -1576,6 +1579,7 @@ def deleterequest(): # #==================================================================# def inlineedit(chunk, data): + vars.recentedit = True chunk = int(chunk) if(chunk == 0): if(len(data.strip()) == 0): @@ -1592,6 +1596,7 @@ def inlineedit(chunk, data): # #==================================================================# def inlinedelete(chunk): + vars.recentedit = True chunk = int(chunk) # Don't delete prompt if(chunk == 0): From 03c1a3ebf963c76db320aa1fafe67ca6282f8c1e Mon Sep 17 00:00:00 2001 From: Gnome Ann <> Date: Tue, 28 Sep 2021 01:10:20 -0400 Subject: [PATCH 9/9] Put vars.recentedit = True in deleterequest() for consistency --- aiserver.py | 1 + 1 file changed, 1 insertion(+) diff --git a/aiserver.py b/aiserver.py index 7ee18541..765727f4 100644 --- a/aiserver.py +++ b/aiserver.py @@ -1565,6 +1565,7 @@ def editsubmit(data): # #==================================================================# def deleterequest(): + vars.recentedit = True # Don't delete prompt if(vars.editln == 0): # Send error message