+
Output
@@ -33,16 +44,12 @@
-
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;
-
-
-
Code editor
-
+import * as lib from "lib"; lib.clear("green"); @@ -61,7 +68,6 @@ function loop(deltaT) { lib.startGameLoop(loop); -return 5;