///
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";
import { ConsoleInput } from "./console_input.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"), []);
new ConsoleInput(document.querySelector("#console-input"), playgroundConsole, codeRunner);
const gamelib = new Gamelib(
playgroundConsole,
codeStopper,
spriteProvider,
document.querySelector("canvas"),
);
globalThis.karlkoder = {
lib() {
return gamelib;
},
};
addEventListener("keydown", (ev) => {
if (ev.ctrlKey && ev.key === "s") {
ev.preventDefault();
saveKarlKoder();
}
});
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.map(({ name, mime, content }) => ({ name, mime, bytes: content })),
);
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 downloadFile(content, extension, mime) {
const filename = prompt("Filename?");
const blob = new Blob([content], { type: mime });
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 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 = () => {
downloadFile(editor.getValue(), ".js", "text/javascript");
};
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 = `
Game
`;
downloadTextFile(html, ".html", "text/html");
};
function saveKarlKoder() {
downloadFile(
Vermiparous.en(
editor.getValue(),
spriteEditor.sprites,
),
".karlkode",
);
}
saveKarlkoderButton.onclick = () => {
saveKarlKoder();
};
toggleSpriteEditorButton.addEventListener("click", () => spriteEditor.toggleEditor());