223 lines
6.2 KiB
JavaScript
223 lines
6.2 KiB
JavaScript
/// <reference types="ace" />
|
|
|
|
import { Debounce } from "./debounce.js";
|
|
import { PlaygroundConsole } from "./playground_console.js";
|
|
import { CodeRunner } from "./code_runner.js";
|
|
import { SpriteEditor } from "./sprite_editor.js";
|
|
import { SpriteProvider } from "./sprite_provider.js";
|
|
import { Gamelib } from "./gamelib.js";
|
|
import { CodeStopper } from "./code_stopper.js";
|
|
import { Vermiparous } from "./vermiparous.js";
|
|
import { promptUpload } from "./prompt_upload.js";
|
|
import { GamelibCompleter } from "./gamelib_completer.js";
|
|
import { TextCompleter } from "./text_completer.js";
|
|
|
|
const playgroundConsole = new PlaygroundConsole(
|
|
document.querySelector("#console-code"),
|
|
);
|
|
|
|
const codeStopper = new CodeStopper();
|
|
|
|
const spriteProvider = new SpriteProvider();
|
|
|
|
const codeRunner = new CodeRunner(playgroundConsole, codeStopper);
|
|
const spriteEditor = new SpriteEditor(document.querySelector("#sprite-editor-container"), []);
|
|
|
|
const gamelib = new Gamelib(
|
|
playgroundConsole,
|
|
codeStopper,
|
|
spriteProvider,
|
|
document.querySelector("canvas"),
|
|
);
|
|
|
|
globalThis.karlkoder = {
|
|
lib() {
|
|
return gamelib;
|
|
},
|
|
};
|
|
|
|
const editor = ace.edit("editor");
|
|
editor.setTheme("ace/theme/gruvbox");
|
|
editor.session.setMode("ace/mode/javascript");
|
|
|
|
const langTools = ace.require("ace/ext/language_tools");
|
|
|
|
editor.setOptions({
|
|
enableBasicAutocompletion: true,
|
|
enableLiveAutocompletion: true,
|
|
});
|
|
|
|
langTools.setCompleters([new GamelibCompleter(), new TextCompleter()]);
|
|
|
|
editor.setValue(sessionStorage.getItem("code") ?? editor.getValue(), -1);
|
|
|
|
const importButton = document.querySelector("#import-button");
|
|
const runButton = document.querySelector("#run-button");
|
|
const saveButton = document.querySelector("#save-button");
|
|
const saveDropdown = document.querySelector("#save-dropdown");
|
|
const saveJsButton = document.querySelector("#save-js");
|
|
const saveHtmlButton = document.querySelector("#save-html");
|
|
const saveKarlkoderButton = document.querySelector("#save-karlkoder");
|
|
const toggleSpriteEditorButton = document.querySelector("#toggle-sprite-editor-button");
|
|
|
|
const sessionSaveDebounce = new Debounce(1000);
|
|
editor.addEventListener("change", () => {
|
|
sessionSaveDebounce.run(() => {
|
|
sessionStorage.setItem("code", editor.getValue());
|
|
});
|
|
});
|
|
|
|
importButton.onclick = async () => {
|
|
const files = await promptUpload(".karlkode", false);
|
|
if (files.length === 0) {
|
|
return;
|
|
}
|
|
if (files.length > 1) {
|
|
throw new Error(
|
|
`unreachable: something went wrong ! files.length > 1 : files.length = ${files.length}`,
|
|
);
|
|
}
|
|
const items = Vermiparous.de(await fetch(URL.createObjectURL(files[0])).then((x) => x.bytes()));
|
|
const sprites = items
|
|
.filter((x) => x.tag === "asset")
|
|
.map((x) => {
|
|
delete x.tag;
|
|
return x;
|
|
});
|
|
const code = items.find((x) => x.tag === "code");
|
|
delete code.tag;
|
|
spriteEditor.importSprites(sprites);
|
|
const dec = new TextDecoder();
|
|
editor.setValue(dec.decode(code.content));
|
|
};
|
|
|
|
runButton.onclick = () => {
|
|
const code = editor.getValue();
|
|
|
|
karlkoder.lib().spriteProvider.injectSprites(spriteEditor.sprites);
|
|
codeRunner.setCode(code);
|
|
codeRunner.toggle();
|
|
document.querySelector("canvas").focus();
|
|
|
|
if (codeRunner.isRunning) {
|
|
runButton.textContent = "✋ Stop";
|
|
runButton.classList.add("active");
|
|
} else {
|
|
runButton.textContent = "🏃 Run";
|
|
runButton.classList.remove("active");
|
|
}
|
|
};
|
|
|
|
function downloadBinaryFile(content, extension) {
|
|
const filename = prompt("Filename?");
|
|
|
|
const blob = new Blob([content]);
|
|
const url = URL.createObjectURL(blob);
|
|
|
|
const element = document.createElement("a");
|
|
|
|
element.href = url;
|
|
element.download = filename.endsWith(extension) ? filename : filename + extension;
|
|
element.style.display = "none";
|
|
|
|
document.body.appendChild(element);
|
|
|
|
element.click();
|
|
|
|
document.body.removeChild(element);
|
|
}
|
|
|
|
function downloadTextFile(content, mime, extension) {
|
|
const filename = prompt("Filename?");
|
|
|
|
const element = document.createElement("a");
|
|
|
|
element.href = `data:${mime};charset=utf-8,${encodeURIComponent(content)}`;
|
|
element.download = filename.endsWith(extension) ? filename : filename + extension;
|
|
element.style.display = "none";
|
|
|
|
document.body.appendChild(element);
|
|
|
|
element.click();
|
|
|
|
document.body.removeChild(element);
|
|
}
|
|
|
|
function minifyJs(code) {
|
|
return code
|
|
.replace(/[\s\n]+/g, " ")
|
|
.replace(/;\s+/g, ";");
|
|
}
|
|
|
|
saveButton.onclick = () => {
|
|
if (saveButton.classList.contains("active")) {
|
|
saveButton.classList.remove("active");
|
|
saveDropdown.style.display = "none";
|
|
} else {
|
|
saveButton.classList.add("active");
|
|
|
|
saveDropdown.style.display = "block";
|
|
}
|
|
};
|
|
|
|
saveJsButton.onclick = () => {
|
|
downloadTextFile(editor.getValue(), "text/javascript", ".js");
|
|
};
|
|
|
|
saveHtmlButton.onclick = async () => {
|
|
const js = editor.getValue()
|
|
.split("\n")
|
|
.map((line) => " ".repeat(12) + line).join("\n");
|
|
let lib = await (await fetch("./js/lib.js")).text();
|
|
lib = minifyJs(lib);
|
|
|
|
const html = `
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="generator" content="karlkoder playground">
|
|
<title>Game</title>
|
|
<style>
|
|
body {
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
height: 100vh;
|
|
margin: 0;
|
|
}
|
|
|
|
|
|
An iterable object such as an Array, having ArrayBuffers, TypedArrays, DataViews, Blobs, strings, or a mix of any of such elements, that will be put inside the Blob. Strings should be well-formed Unicode, and lone surrogates are sanitized using the same algorithm as String.prototype.toWellFormed().
|
|
</style>
|
|
<script type="importmap">
|
|
{
|
|
"imports": {
|
|
"lib": "data:text/javascript,${encodeURIComponent(lib)}"
|
|
}
|
|
}
|
|
</script>
|
|
<script type="module">
|
|
${js}
|
|
</script>
|
|
</head>
|
|
<body>
|
|
<canvas width="480" height="360"></canvas>
|
|
</body>
|
|
</html>`;
|
|
|
|
downloadTextFile(html, "text/html", ".html");
|
|
};
|
|
|
|
saveKarlkoderButton.onclick = () => {
|
|
downloadBinaryFile(
|
|
Vermiparous.en(
|
|
editor.getValue(),
|
|
spriteEditor.sprites,
|
|
),
|
|
".karlkode",
|
|
);
|
|
};
|
|
|
|
toggleSpriteEditorButton.addEventListener("click", () => spriteEditor.toggleEditor());
|