fix various stuff
This commit is contained in:
parent
4087c6ade8
commit
c21497bad1
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
node_modules/
|
22
index.html
22
index.html
@ -7,13 +7,24 @@
|
|||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||||
<title>Document</title>
|
<title>Document</title>
|
||||||
<link rel="stylesheet" href="./style.css">
|
<link rel="stylesheet" href="./style.css">
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.43.2/ace.js" type="text/javascript" charset="utf-8"></script>
|
<script type="importmap">
|
||||||
<script src="./index.js" defer></script>
|
{
|
||||||
|
"imports": {
|
||||||
|
"lib": "./lib.js"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.43.2/ace.js"></script>
|
||||||
|
<script src="./index.js" type="module"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<main>
|
<main>
|
||||||
|
|
||||||
<div class="column" style="flex: 1">
|
<div class="column" style="flex: 1">
|
||||||
|
<div id="buttons">
|
||||||
|
<button id="run-button">▶️ Run</button>
|
||||||
|
<button id="save-button">💾 Save/Export</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<div class="section-header">Output</div>
|
<div class="section-header">Output</div>
|
||||||
@ -33,16 +44,12 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="column" style="flex: 3">
|
<div class="column" style="flex: 3">
|
||||||
<div>
|
|
||||||
<button id="start-stop">Start</button>
|
|
||||||
<button id="save">Save</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<div class="section-header">Code editor</div>
|
<div class="section-header">Code editor</div>
|
||||||
|
|
||||||
<div id="editor-area">
|
<div id="editor-area">
|
||||||
<pre id="editor">
|
<pre id="editor">import * as lib from "lib";
|
||||||
|
|
||||||
lib.clear("green");
|
lib.clear("green");
|
||||||
|
|
||||||
@ -61,7 +68,6 @@ function loop(deltaT) {
|
|||||||
|
|
||||||
lib.startGameLoop(loop);
|
lib.startGameLoop(loop);
|
||||||
|
|
||||||
return 5;
|
|
||||||
</pre>
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
150
index.js
150
index.js
@ -1,91 +1,75 @@
|
|||||||
"use strict";
|
/// <reference types="ace" />
|
||||||
|
|
||||||
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) {
|
async function runCode(code, consoleCode) {
|
||||||
lib;
|
|
||||||
consoleCode.textContent += `\nRunning code....\n`;
|
consoleCode.textContent += `\nRunning code....\n`;
|
||||||
try {
|
try {
|
||||||
const result = await eval(`(async function () {${code}})()`);
|
const module = await import(`data:text/javascript;charset=utf-8,${encodeURIComponent(code)}`);
|
||||||
consoleCode.textContent += `Code returned ${result}\n`;
|
module.default?.();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
consoleCode.textContent += `${error}\n`;
|
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);
|
||||||
|
};
|
||||||
|
38
lib.js
Normal file
38
lib.js
Normal file
@ -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);
|
||||||
|
}
|
19
package-lock.json
generated
Normal file
19
package-lock.json
generated
Normal file
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
5
package.json
Normal file
5
package.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/ace": "^0.0.52"
|
||||||
|
}
|
||||||
|
}
|
16
style.css
16
style.css
@ -1,11 +1,12 @@
|
|||||||
* {
|
* {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
color-scheme: light dark;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
background-color: #EEE;
|
background-color: light-dark(#EEE, #333);
|
||||||
}
|
}
|
||||||
|
|
||||||
main {
|
main {
|
||||||
@ -19,7 +20,7 @@ main {
|
|||||||
background-color: white;
|
background-color: white;
|
||||||
border-top-left-radius: 8px;
|
border-top-left-radius: 8px;
|
||||||
border-top-right-radius: 8px;
|
border-top-right-radius: 8px;
|
||||||
border: 1px solid #BDBDBD;
|
border: 1px solid light-dark(#BDBDBD, #222);
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
padding-left: 8px;
|
padding-left: 8px;
|
||||||
font-family: sans-serif;
|
font-family: sans-serif;
|
||||||
@ -41,6 +42,17 @@ section *:last-child {
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div#buttons {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
div#buttons > * {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.column {
|
.column {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user