From ca5f89b7e70e4bd25eb56c0163ac972bdf1ad1e5 Mon Sep 17 00:00:00 2001 From: nimec01 <24428341+nimec01@users.noreply.github.com> Date: Wed, 13 May 2026 11:46:58 +0200 Subject: [PATCH 1/3] persist loaded code locally and refactor internal implementation --- web/js/codeworld.js | 58 +++++++----------------------- web/js/codeworld_shared.js | 73 ++++++++++++++++++++++++++++++++++---- web/js/run.js | 25 +++++++++---- web/run.html | 15 ++++++++ 4 files changed, 113 insertions(+), 58 deletions(-) diff --git a/web/js/codeworld.js b/web/js/codeworld.js index b5dbdbef..2b1d5797 100644 --- a/web/js/codeworld.js +++ b/web/js/codeworld.js @@ -30,7 +30,9 @@ import { run, toggleObsoleteCodeAlert, warnIfUnsaved, - sha256digest, + saveCodeToLocalStorageAndReplaceHash, + tryLoadingCodeFromLocalStorage, + tryFetchCodeFromSourceAndStripURL, } from './codeworld_shared.js'; import * as Alert from './utils/alert.js'; @@ -178,7 +180,7 @@ async function init() { if(window.buildMode === 'codeworld') document.querySelector("#docButton").style.display = "none"; - const savedCode = localStorage.getItem(`${window.buildMode}-${window.location.hash.slice(1)}`); + const savedCode = tryLoadingCodeFromLocalStorage(window.buildMode); if (savedCode) { setCode(savedCode); @@ -192,48 +194,15 @@ picture = ... `); } - const currentUrl = new URL(window.location); - const searchParams = currentUrl.searchParams; - const codeSrc = searchParams.get("loadSrc"); - if(codeSrc) { - const fetchController = new AbortController(); - sweetAlert({ - title: Alert.title('Loading code'), - text: 'The code is being fetched. Please wait...', - onOpen: () => { - sweetAlert.showLoading(); - sweetAlert.getCancelButton().disabled = false; - }, - showConfirmButton: false, - showCancelButton: true, - showCloseButton: false, - allowOutsideClick: false, - allowEscapeKey: false, - allowEnterKey: false, - }).then(() => { - fetchController.abort(); - }); - try { - const response = await fetch(codeSrc, { - signal: fetchController.signal, - }); - const code = await response.text(); - setCode(code); - sweetAlert.close(); - searchParams.delete("loadSrc"); - window.history.replaceState(window.history.state, "", currentUrl.toString()); - } catch (error) { - sweetAlert( - 'Oops!', - 'Could not load the code from source. Please try again.', - 'error' - ); - } - } + await tryFetchCodeFromSourceAndStripURL(async (code) => { + setCode(code); + saveCodeToLocalStorageAndReplaceHash(code, window.buildMode); + }); if(window.preloadCode && window.buildMode === 'haskell'){ const codeToLoad = new DOMParser().parseFromString(window.preloadCode, 'text/html').documentElement.textContent; setCode(codeToLoad); + await saveCodeToLocalStorageAndReplaceHash(codeToLoad, window.buildMode); }; } @@ -959,7 +928,7 @@ function stopRun() { } window.cancelCompile(); - run('', '', '', false, null); + run(false, '', false, null); } function compile() { @@ -1020,12 +989,9 @@ function compile() { compilerMessage = 'Sorry! Your program couldn\'t be run right now.'; } - const codeHash = await sha256digest(src.trim()); - window.program = compiledProgram; - run(codeHash,"deploy_hash",compilerMessage,false,compileGeneration); - localStorage.setItem(`${window.buildMode}-${codeHash}`, src); - + run(status === 200,compilerMessage,false,compileGeneration); + await saveCodeToLocalStorageAndReplaceHash(src, window.buildMode); sweetAlert.close(); return; diff --git a/web/js/codeworld_shared.js b/web/js/codeworld_shared.js index b1451df8..687e60ae 100644 --- a/web/js/codeworld_shared.js +++ b/web/js/codeworld_shared.js @@ -949,7 +949,7 @@ function initializeLayoutContainer(options) { } -function run(hash, dhash, msg, error, generation) { +function run(successful, msg, error, generation) { window.runningGeneration = generation; window.debugAvailable = false; window.debugActive = false; @@ -965,17 +965,13 @@ function run(hash, dhash, msg, error, generation) { '*' ); - if (hash) { - window.location.hash = `#${hash}`; - } - runner.contentWindow.location.replace(`run?mode=${window.buildMode}`); document.getElementById('runner').style.display = 'none'; document.getElementById('startRecButton').style.display = 'none'; const layoutHandler = $(LAYOUT_CONTAINER_CLASSNAME).layout(); - if (hash || msg) { + if (successful || msg) { layoutHandler.show('east'); layoutHandler.open('east'); } else { @@ -1097,6 +1093,68 @@ async function sha256digest(data) { }); } +async function saveCodeToLocalStorageAndReplaceHash(code, mode) { + const currentUrl = new URL(window.location); + const searchParams = currentUrl.searchParams; + + const codeHash = await sha256digest(code.trim()); + localStorage.setItem(`${mode}-${codeHash}`, code); + currentUrl.hash = codeHash; + + window.history.replaceState(window.history.state, "", currentUrl.toString()); +} + +function tryLoadingCodeFromLocalStorage(mode) { + const currentUrl = new URL(window.location); + const codeHash = currentUrl.hash.slice(1); + if(!codeHash) return; + + return localStorage.getItem(`${mode}-${codeHash}`); +} + +async function tryFetchCodeFromSourceAndStripURL(handler){ + const currentUrl = new URL(window.location); + const searchParams = currentUrl.searchParams; + + const codeSrc = searchParams.get("loadSrc"); + if (!codeSrc) return; + + const fetchController = new AbortController(); + sweetAlert({ + title: Alert.title('Loading code'), + text: 'The code is being fetched. Please wait...', + onOpen: () => { + sweetAlert.showLoading(); + sweetAlert.getCancelButton().disabled = false; + }, + showConfirmButton: false, + showCancelButton: true, + showCloseButton: false, + allowOutsideClick: false, + allowEscapeKey: false, + allowEnterKey: false, + }).then(() => { + fetchController.abort(); + }); + try { + const response = await fetch(codeSrc, { + signal: fetchController.signal, + }); + const code = await response.text(); + searchParams.delete("loadSrc"); + window.history.replaceState(window.history.state, "", currentUrl.toString()); + sweetAlert.close(); + handler(code); + + } catch (error) { + sweetAlert( + 'Oops!', + 'Could not load the code from source. Please try again.', + 'error' + ); + } +} + export { clearMessages, definePanelExtension, @@ -1114,4 +1172,7 @@ export { toggleObsoleteCodeAlert, warnIfUnsaved, sha256digest, + saveCodeToLocalStorageAndReplaceHash, + tryLoadingCodeFromLocalStorage, + tryFetchCodeFromSourceAndStripURL, }; diff --git a/web/js/run.js b/web/js/run.js index 6fe7da80..0eb1a61d 100644 --- a/web/js/run.js +++ b/web/js/run.js @@ -15,6 +15,12 @@ */ import { sendHttp } from './utils/network.js'; +import { + saveCodeToLocalStorageAndReplaceHash, + tryLoadingCodeFromLocalStorage, + tryFetchCodeFromSourceAndStripURL, +} from "./codeworld_shared.js" +import * as Alert from './utils/alert.js'; // Tracks when the program started, and whether the program has done // anything observable (as best we can tell). This is used to decide @@ -233,6 +239,7 @@ function start() { } async function init() { + await Alert.init(); let paramList = location.search.slice(1).split('&'); const params = {}; for (let i = 0; i < paramList.length; i++) { @@ -250,6 +257,11 @@ async function init() { let mode = params['mode']; if(!mode) mode = 'codeworld'; + + const savedCode = tryLoadingCodeFromLocalStorage(mode); + if(savedCode) { + window.preloadCode = savedCode; + } const codeSrc = params['loadSrc']; @@ -257,12 +269,13 @@ async function init() { try { let code = window.preloadCode; if(!code) { - const response = await fetch(codeSrc); - code = await response.text(); - if(!response.ok) { - throw new Error("Could not load source code from external location. Response code not OK."); - } - } + await tryFetchCodeFromSourceAndStripURL((fetchedCode) => { + code = fetchedCode; + }); + } + + await saveCodeToLocalStorageAndReplaceHash(code, mode); + const data = new FormData(); data.append('source', code); data.append('mode', mode); diff --git a/web/run.html b/web/run.html index 98e32db1..ffcdccc3 100644 --- a/web/run.html +++ b/web/run.html @@ -5,10 +5,25 @@ + + + +
+