+
+ Export as Karlkoder File
+
diff --git a/src/index.js b/src/index.js
index 2108071..6615e77 100644
--- a/src/index.js
+++ b/src/index.js
@@ -7,6 +7,8 @@ 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";
const playgroundConsole = new PlaygroundConsole(
document.querySelector("#console-code"),
@@ -38,11 +40,13 @@ editor.session.setMode("ace/mode/javascript");
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);
@@ -52,6 +56,17 @@ editor.addEventListener("change", (ev) => {
});
});
+importButton.onclick = async (ev) => {
+ const files = await promptUpload("", false);
+ if (files.length === 0) {
+ return;
+ }
+ if (files.length > 1) {
+ throw new Error("unreachable: something went wrong !");
+ }
+ const item = Vermiparous.de(await fetch(URL.createObjectURL(files[0])).then((x) => x.bytes()));
+};
+
runButton.onclick = (ev) => {
const code = editor.getValue();
@@ -148,6 +163,19 @@ ${js}
downloadFile(html, "text/html", ".html");
};
+saveKarlkoderButton.onclick = (ev) => {
+ downloadFile(
+ Vermiparous.en(
+ editor.getValue()
+ .split("\n")
+ .map((line) => " ".repeat(12) + line).join("\n"),
+ spriteEditor.sprites,
+ ),
+ "",
+ ".karlkode",
+ );
+};
+
toggleSpriteEditorButton.addEventListener("click", () => {
const cont = document.querySelector("#sprite-editor-container");
if (cont.style.getPropertyValue("display") === "none") {
diff --git a/src/prompt_upload.js b/src/prompt_upload.js
new file mode 100644
index 0000000..0c53a34
--- /dev/null
+++ b/src/prompt_upload.js
@@ -0,0 +1,19 @@
+export function promptUpload(accept, multiple) {
+ return new Promise((resolve) => {
+ const input = document.createElement("input");
+ input.type = "file";
+ input.accept = accept;
+ input.multiple = multiple;
+ input.style = "display: none;";
+ input.addEventListener("cancel", () => {
+ resolve([]);
+ input.remove();
+ });
+ input.addEventListener("change", () => {
+ resolve(input.files);
+ input.remove();
+ });
+ document.body.append(input);
+ input.click();
+ });
+}
diff --git a/src/sprite_editor.js b/src/sprite_editor.js
index cc19918..a37b204 100644
--- a/src/sprite_editor.js
+++ b/src/sprite_editor.js
@@ -1,3 +1,5 @@
+import { promptUpload } from "./prompt_upload.js";
+
export class SpriteEditor {
constructor(rootEl, sprites) {
this.list = rootEl.querySelector("#sprite-editor-sprite-list");
@@ -13,39 +15,26 @@ export class SpriteEditor {
this.renderList();
}
- promptUpload() {
- const input = document.createElement("input");
- input.type = "file";
- input.accept = "image/*";
- input.multiple = true;
- input.style = "display: none;";
- input.addEventListener("cancel", () => {
- input.remove();
- });
- input.addEventListener("change", async () => {
- for (const file of input.files) {
- const rootName = file.name;
- let fullName = file.name;
- for (let i = 0; this.sprites.some((x) => x.name === fullName); ++i) {
- const extensionIdx = rootName.split("").findLastIndex((x) => x === ".");
- let name = rootName;
- let extension = "";
- if (extensionIdx !== -1) {
- name = rootName.slice(0, extensionIdx);
- extension = rootName.slice(extensionIdx);
- }
- fullName = `${name}-${i}${extension}`;
+ async promptUpload() {
+ for (const file of await promptUpload("image/*", true)) {
+ const rootName = file.name;
+ let fullName = file.name;
+ for (let i = 0; this.sprites.some((x) => x.name === fullName); ++i) {
+ const extensionIdx = rootName.split("").findLastIndex((x) => x === ".");
+ let name = rootName;
+ let extension = "";
+ if (extensionIdx !== -1) {
+ name = rootName.slice(0, extensionIdx);
+ extension = rootName.slice(extensionIdx);
}
- this.addSprite({
- name: fullName,
- mime: file.type,
- bytes: await fetch(URL.createObjectURL(file)).then((x) => x.bytes()),
- });
+ fullName = `${name}-${i}${extension}`;
}
- input.remove();
- });
- document.body.append(input);
- input.click();
+ this.addSprite({
+ name: fullName,
+ mime: file.type,
+ bytes: await fetch(URL.createObjectURL(file)).then((x) => x.bytes()),
+ });
+ }
}
addSprite({ name, bytes, mime }) {
diff --git a/src/vermiparous.ts b/src/vermiparous.js
similarity index 79%
rename from src/vermiparous.ts
rename to src/vermiparous.js
index b985a3d..ee2f0e5 100644
--- a/src/vermiparous.ts
+++ b/src/vermiparous.js
@@ -2,43 +2,30 @@ const KEYWORDS = ["asset", "code"];
const MIN_KW_LENGTH = Math.min(...KEYWORDS.map((x) => x.length));
const SEMICOLON_CHARCODE = ";".charCodeAt(0);
-function isKeyword(buffer: number[]) {
- const kw = String.fromCharCode(...buffer);
+function isKeyword(buffer) {
+ const kw = buffer.map((x) => String.fromCharCode(x)).join("");
return KEYWORDS.includes(kw);
}
-function strToBytes(str: string): number[] {
+function strToBytes(str) {
return str.split("").map((x) => x.charCodeAt(0));
}
-type EncAsset = {
- name: string;
- mime: string;
- content: Uint8Array;
-};
-
-type DecAssic = {
- tag: "code" | "asset";
- name: string;
- mime: string;
- content: Uint8Array;
-};
-
export class Vermiparous {
- private constructor() {}
- static en(code: string, assets: EncAsset[]): Uint8Array {
- const ret: number[] = [];
+ static en(code, assets) {
+ const ret = [];
for (const asset of assets) {
+ console.log(asset);
ret.push(...strToBytes("asset"));
ret.push(...strToBytes(asset.name.length.toString()));
ret.push(...strToBytes(";"));
ret.push(...strToBytes(asset.mime.length.toString()));
ret.push(...strToBytes(";"));
- ret.push(...strToBytes(asset.content.length.toString()));
+ ret.push(...strToBytes(asset.bytes.length.toString()));
ret.push(...strToBytes(";"));
ret.push(...strToBytes(asset.name));
ret.push(...strToBytes(asset.mime));
- ret.push(...asset.content);
+ ret.push(...asset.bytes);
}
ret.push(...strToBytes("code"));
ret.push(...strToBytes("0;0;"));
@@ -48,7 +35,7 @@ export class Vermiparous {
return new Uint8Array(ret);
}
- static de(bytes: Uint8Array): DecAssic[] {
+ static de(bytes) {
const ret = [];
let buffer = [];
let idx = 0;
@@ -92,7 +79,7 @@ export class Vermiparous {
}
const [name, mime, content] = consoom;
ret.push({
- tag: String.fromCharCode(...tag) as DecAssic["tag"],
+ tag: String.fromCharCode(...tag),
name: String.fromCharCode(...name),
mime: String.fromCharCode(...mime),
content: new Uint8Array(content),