diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c2658d7 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules/ diff --git a/index.html b/index.html index da2ad97..1098305 100644 --- a/index.html +++ b/index.html @@ -7,13 +7,24 @@ Document - - + + +
+
+ + +
Output
@@ -33,16 +44,12 @@
-
- - -
Code editor
-
+					
import * as lib from "lib";
 
 lib.clear("green");
 
@@ -61,7 +68,6 @@ function loop(deltaT) {
 
 lib.startGameLoop(loop);
 
-return 5;
 
diff --git a/index.js b/index.js index 1716e81..d4da91e 100644 --- a/index.js +++ b/index.js @@ -1,91 +1,75 @@ -"use strict"; - -let _gameLoopTimeout = undefined; - -(function () { - const editor = ace.edit("editor"); - editor.setTheme("ace/theme/gruvbox"); - editor.session.setMode("ace/mode/javascript"); - - if (editor.getValue() === "") { - editor.setValue( - ``, - ); - } - - let running = false; - - const startStopButton = document.querySelector("#start-stop"); - const saveButton = document.querySelector("#save"); - const consoleCode = document.querySelector("#console-code"); - - startStopButton.onclick = (ev) => { - if (running) { - if (_gameLoopTimeout) { - clearTimeout(_gameLoopTimeout); - _gameLoopTimeout = undefined; - } - startStopButton.textContent = "Start"; - running = false; - } else { - const code = editor.getValue(); - runCode(code, consoleCode); - startStopButton.textContent = "Stop"; - running = true; - } - }; -})(); - -const lib = (() => { - const consoleCode = document.querySelector("#console-code"); - - const width = 480; - const height = 360; - - const canvas = document.querySelector("canvas"); - canvas.width = width; - canvas.height = height; - const cx = canvas.getContext("2d"); - cx.imageSmoothingEnabled = false; - - function rgb(red, green, blue) { - return `rgb(${red}, ${green}, ${blue})`; - } - - function clear(color) { - cx.fillStyle = color; - cx.fillRect(0, 0, width, height); - } - - function drawRect(x, y, width, height, color) { - cx.fillStyle = color; - cx.fillRect(x, y, width, height); - } - - function println(msg) { - consoleCode.textContent += `${msg}\n`; - } - - function startGameLoop(loopFunction) { - let before = Date.now(); - _gameLoopTimeout = setInterval(() => { - const now = Date.now(); - const deltaT = (now - before) / 1000; - before = now; - loopFunction(deltaT); - }, 16); - } - - return { width, height, rgb, clear, drawRect, println, startGameLoop }; -})(); +/// async function runCode(code, consoleCode) { - lib; consoleCode.textContent += `\nRunning code....\n`; try { - const result = await eval(`(async function () {${code}})()`); - consoleCode.textContent += `Code returned ${result}\n`; + const module = await import(`data:text/javascript;charset=utf-8,${encodeURIComponent(code)}`); + module.default?.(); } catch (error) { consoleCode.textContent += `${error}\n`; } } + +class Debounce { + timer = null + lastCall = 0; + + constructor(timeout) { + this.timeout = timeout; + } + + run(fn) { + const now = Date.now(); + if (this.timer === null && now - this.lastCall > this.timeout) { + fn(); + this.lastCall = now; + return; + } + if (this.timer !== null) { + clearTimeout(this.timer); + this.timer = null; + } + this.timer = setTimeout(() => { + fn(); + this.lastCall = Date.now(); + this.timer = null; + }, this.timeout) + } +} + + +const editor = ace.edit("editor"); +editor.setTheme("ace/theme/gruvbox"); +editor.session.setMode("ace/mode/javascript"); + +editor.setValue(sessionStorage.getItem("code") ?? editor.getValue(), -1); + +const runButton = document.querySelector("#run-button"); +const saveButton = document.querySelector("#save-button"); +const consoleCode = document.querySelector("#console-code"); + +const sessionSaveDebounce = new Debounce(1000); +editor.addEventListener("change", (ev) => { + sessionSaveDebounce.run(() => { + sessionStorage.setItem("code", editor.getValue()) + }) +}) + +runButton.onclick = (ev) => { + const code = editor.getValue(); + runCode(code, consoleCode); + runButton.textContent = "⚙️ Running..."; +}; + + +saveButton.onclick = (ev) => { + const code = editor.getValue(); + const element = document.createElement("a"); + element.setAttribute("href", `data:text/javascript;charset=utf-8,${encodeURIComponent(code)}`); + const filename = prompt("Filename?") + element.setAttribute("download", filename.endsWith(".js") ? filename : `${filename}.js`); + element.style.display = "none"; + document.body.appendChild(element); + element.click(); + document.body.removeChild(element); +}; diff --git a/lib.js b/lib.js new file mode 100644 index 0000000..582e588 --- /dev/null +++ b/lib.js @@ -0,0 +1,38 @@ +const consoleCode = document.querySelector("#console-code"); + +export const width = 480; +export const height = 360; + +const canvas = document.querySelector("canvas"); +canvas.width = width; +canvas.height = height; +const cx = canvas.getContext("2d"); +cx.imageSmoothingEnabled = false; + +export function rgb(red, green, blue) { + return `rgb(${red}, ${green}, ${blue})`; +} + +export function clear(color) { + cx.fillStyle = color; + cx.fillRect(0, 0, width, height); +} + +export function drawRect(x, y, width, height, color) { + cx.fillStyle = color; + cx.fillRect(x, y, width, height); +} + +export function println(msg) { + consoleCode.textContent += `${msg}\n`; +} + +export function startGameLoop(loopFunction) { + let before = Date.now(); + setInterval(() => { + const now = Date.now(); + const deltaT = (now - before) / 1000; + before = now; + loopFunction(deltaT); + }, 16); +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..e3dbb1d --- /dev/null +++ b/package-lock.json @@ -0,0 +1,19 @@ +{ + "name": "karlkoder-playground", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "devDependencies": { + "@types/ace": "^0.0.52" + } + }, + "node_modules/@types/ace": { + "version": "0.0.52", + "resolved": "https://registry.npmjs.org/@types/ace/-/ace-0.0.52.tgz", + "integrity": "sha512-YPF9S7fzpuyrxru+sG/rrTpZkC6gpHBPF14W3x70kqVOD+ks6jkYLapk4yceh36xej7K4HYxcyz9ZDQ2lTvwgQ==", + "dev": true, + "license": "MIT" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..b1a6469 --- /dev/null +++ b/package.json @@ -0,0 +1,5 @@ +{ + "devDependencies": { + "@types/ace": "^0.0.52" + } +} diff --git a/style.css b/style.css index f8854ac..6309120 100644 --- a/style.css +++ b/style.css @@ -1,11 +1,12 @@ * { box-sizing: border-box; + color-scheme: light dark; } body { margin: 0; height: 100vh; - background-color: #EEE; + background-color: light-dark(#EEE, #333); } main { @@ -19,7 +20,7 @@ main { background-color: white; border-top-left-radius: 8px; border-top-right-radius: 8px; - border: 1px solid #BDBDBD; + border: 1px solid light-dark(#BDBDBD, #222); padding: 4px; padding-left: 8px; font-family: sans-serif; @@ -41,6 +42,17 @@ section *:last-child { flex: 1; } +div#buttons { + width: 100%; + display: flex; + flex: row; + justify-content: space-between; + gap: 5px; +} +div#buttons > * { + width: 100%; +} + .column { display: flex; flex-direction: column;