158 lines
4.7 KiB
JavaScript

/// <reference types="ace" />
import { Debounce } from "./debounce.js";
import { PlaygroundConsole } from "./playground_console.js";
import { CodeRunner } from "./code_runner.js";
import { AssetEditor } from "./asset_editor.js";
import { AssetProvider } from "./asset_provider.js";
import { Gamelib } from "./gamelib.js";
import { CodeStopper } from "./code_stopper.js";
import { KarlkoderCodec } from "./karlkoder_codec.js";
import { promptUpload } from "./prompt_upload.js";
import { GamelibCompleter } from "./gamelib_completer.js";
import { TextCompleter } from "./text_completer.js";
import { ConsoleInput } from "./console_input.js";
import { downloadFile, slugify } from "./utils.js";
import { HtmlExporter } from "./html_exporter.js";
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,
copyWithEmptySelection: true,
});
langTools.setCompleters([new GamelibCompleter(), new TextCompleter()]);
editor.setValue(sessionStorage.getItem("code") ?? editor.getValue(), -1);
const playgroundConsole = new PlaygroundConsole(
document.querySelector("#console-code"),
editor,
);
addEventListener("DOMContentLoaded", () => {
playgroundConsole.getInterface().log("Karlkode 1.0");
});
const codeStopper = new CodeStopper();
const assetProvider = new AssetProvider();
const codeRunner = new CodeRunner(playgroundConsole.getInterface(), codeStopper);
const assetEditor = new AssetEditor(document.querySelector("#asset-editor-container"), []);
new ConsoleInput(document.querySelector("#console-input"), playgroundConsole, codeRunner);
const htmlExporter = new HtmlExporter(assetProvider);
const gamelib = new Gamelib(
playgroundConsole.getInterface(),
codeStopper,
assetProvider,
document.querySelector("canvas"),
);
globalThis.karlkoder = {
lib() {
return gamelib;
},
};
const runButton = document.querySelector("#run-button");
const toggleAssetEditorButton = document.querySelector("#toggle-asset-editor-button");
const projectName = document.querySelector("#project-name");
const saveButton = document.querySelector("#save-button");
const loadButton = document.querySelector("#load-button");
const exportButton = document.querySelector("#export-button");
const sessionSaveDebounce = new Debounce(1000);
editor.addEventListener("change", () => {
sessionSaveDebounce.run(() => {
sessionStorage.setItem("code", editor.getValue());
});
});
loadButton.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 = KarlkoderCodec.de(
await fetch(URL.createObjectURL(files[0])).then((x) => x.bytes()),
);
const assets = items
.filter((x) => x.tag === "asset")
.map((x) => {
delete x.tag;
return x;
});
const code = items.find((x) => x.tag === "code");
delete code.tag;
assetEditor.importAssets(
assets.map(({ name, mime, content }) => ({ name, mime, bytes: content })),
);
projectName.value = code.name;
const dec = new TextDecoder();
editor.setValue(dec.decode(code.content));
};
runButton.onclick = async () => {
const code = editor.getValue();
const assets = await assetEditor.getAssets();
karlkoder.lib().assetProvider.injectAssets(assets);
codeRunner.setCode(code);
codeRunner.toggle();
document.querySelector("canvas").focus();
if (codeRunner.isRunning) {
runButton.textContent = "✋ Stop";
runButton.classList.add("running");
} else {
runButton.textContent = "🏃 Run";
runButton.classList.remove("running");
}
};
exportButton.onclick = async () => {
const html = await htmlExporter.export(projectName.value, editor.getValue());
downloadFile(slugify(projectName.value) || "project", html, ".html", "text/html");
};
function saveKarlKoder() {
downloadFile(
slugify(projectName.value) || "project",
KarlkoderCodec.en(
projectName.value,
editor.getValue(),
assetEditor.assets,
),
".karlkode",
);
}
saveButton.onclick = () => saveKarlKoder();
addEventListener("keydown", (ev) => {
if (ev.ctrlKey && ev.key === "s") {
ev.preventDefault();
saveKarlKoder();
}
});
toggleAssetEditorButton.addEventListener("click", () => assetEditor.toggleEditor());