diff --git a/src/main/resources/static/css/customAssets.css b/src/main/resources/static/css/customAssets.css new file mode 100644 index 0000000..1eab616 --- /dev/null +++ b/src/main/resources/static/css/customAssets.css @@ -0,0 +1,51 @@ +.modal { + display: flex; + justify-content: center; + align-items: center; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100vh; + background-color: rgba(0, 0, 0, 0.45); +} + +.modal .modal-inner { + width: 1024px; + background-color: #0a0e1a; + border-radius: 8px; + padding: 12px; + color: white; + border: 1px solid #504768; + max-height: 90vh; + overflow: auto; +} + +.modal .modal-inner form { + display: flex; + flex-direction: column; + gap: 12px; +} + +.modal .modal-inner .form-group { + display: flex; + flex-direction: column; + gap: 8px; +} + +.modal .modal-inner .form-actions { + display: flex; + justify-content: space-between; +} + +.modal .modal-inner textarea { + max-width: 100%; + resize: vertical; +} + +.modal .modal-inner .form-error { + padding: 8px; + background-color: rgba(200, 0, 0, 0.3); + border: 1px solid #a00; + border-radius: 4px; +} diff --git a/src/main/resources/static/js/customAssets.js b/src/main/resources/static/js/customAssets.js new file mode 100644 index 0000000..45d52e2 --- /dev/null +++ b/src/main/resources/static/js/customAssets.js @@ -0,0 +1,55 @@ +const assetModal = document.getElementById("custom-asset-modal"); +const userSourceTextArea = document.getElementById("custom-asset-code"); +const formErrorWrapper = document.getElementById("custom-asset-error"); +const jsErrorTitle = document.getElementById("js-error-title"); +const jsErrorDetails = document.getElementById("js-error-details"); + +function toggleCustomAssetModal(event) { + if (event !== undefined && event.target !== event.currentTarget) { + return; + } + if (assetModal.classList.contains("hidden")) { + assetModal.classList.remove("hidden"); + } else { + assetModal.classList.add("hidden"); + } +} + +function submitCodeAsset(formEvent) { + formEvent.preventDefault(); + const src = userSourceTextArea.value; + const error = getUserJavaScriptSourceError(src); + if (error) { + jsErrorTitle.textContent = error.title; + jsErrorDetails.textContent = error.details; + formErrorWrapper.classList.remove("hidden"); + return false; + } + formErrorWrapper.classList.add("hidden"); + jsErrorTitle.textContent = ""; + jsErrorDetails.textContent = ""; + return false; +} + +function getUserJavaScriptSourceError(src) { + let ast; + + try { + ast = acorn.parse(src, { + ecmaVersion: "latest", + sourceType: "script", + }); + } catch (e) { + return { title: "Syntax Error", details: e.message }; + } + + const functionNames = ast.body.filter((node) => node.type === "FunctionDeclaration").map((node) => node.id.name); + if (!functionNames.includes("init")) { + return { title: "Missing function: init", details: "Your code must include a function named 'init'." }; + } + if (!functionNames.includes("tick")) { + return { title: "Missing function: tick", details: "Your code must include a function named 'tick'." }; + } + + return undefined; +} diff --git a/src/main/resources/templates/admin.html b/src/main/resources/templates/admin.html index 3661e43..899f9c5 100644 --- a/src/main/resources/templates/admin.html +++ b/src/main/resources/templates/admin.html @@ -7,6 +7,7 @@ + +
@@ -60,6 +62,18 @@ +