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">
|
||||
<title>Document</title>
|
||||
<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 src="./index.js" defer></script>
|
||||
<script type="importmap">
|
||||
{
|
||||
"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>
|
||||
<body>
|
||||
<main>
|
||||
|
||||
<div class="column" style="flex: 1">
|
||||
<div id="buttons">
|
||||
<button id="run-button">▶️ Run</button>
|
||||
<button id="save-button">💾 Save/Export</button>
|
||||
</div>
|
||||
|
||||
<section>
|
||||
<div class="section-header">Output</div>
|
||||
@ -33,16 +44,12 @@
|
||||
</div>
|
||||
|
||||
<div class="column" style="flex: 3">
|
||||
<div>
|
||||
<button id="start-stop">Start</button>
|
||||
<button id="save">Save</button>
|
||||
</div>
|
||||
|
||||
<section>
|
||||
<div class="section-header">Code editor</div>
|
||||
|
||||
<div id="editor-area">
|
||||
<pre id="editor">
|
||||
<pre id="editor">import * as lib from "lib";
|
||||
|
||||
lib.clear("green");
|
||||
|
||||
@ -61,7 +68,6 @@ function loop(deltaT) {
|
||||
|
||||
lib.startGameLoop(loop);
|
||||
|
||||
return 5;
|
||||
</pre>
|
||||
</div>
|
||||
</section>
|
||||
|
150
index.js
150
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 };
|
||||
})();
|
||||
/// <reference types="ace" />
|
||||
|
||||
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);
|
||||
};
|
||||
|
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;
|
||||
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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user