vermiparous
This commit is contained in:
parent
a4f2da333f
commit
2514cd3669
@ -14,6 +14,8 @@
|
|||||||
<main>
|
<main>
|
||||||
<div class="column" style="flex: 1">
|
<div class="column" style="flex: 1">
|
||||||
<div id="buttons">
|
<div id="buttons">
|
||||||
|
<button id="import-button">🙏 Import</button>
|
||||||
|
|
||||||
<a
|
<a
|
||||||
href="docs/index.html"
|
href="docs/index.html"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
@ -42,6 +44,12 @@
|
|||||||
>
|
>
|
||||||
Export as HTML
|
Export as HTML
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
id="save-karlkoder"
|
||||||
|
class="dropdown-option"
|
||||||
|
>
|
||||||
|
Export as Karlkoder File
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
28
src/index.js
28
src/index.js
@ -7,6 +7,8 @@ import { SpriteEditor } from "./sprite_editor.js";
|
|||||||
import { SpriteProvider } from "./sprite_provider.js";
|
import { SpriteProvider } from "./sprite_provider.js";
|
||||||
import { Gamelib } from "./gamelib.js";
|
import { Gamelib } from "./gamelib.js";
|
||||||
import { CodeStopper } from "./code_stopper.js";
|
import { CodeStopper } from "./code_stopper.js";
|
||||||
|
import { Vermiparous } from "./vermiparous.js";
|
||||||
|
import { promptUpload } from "./prompt_upload.js";
|
||||||
|
|
||||||
const playgroundConsole = new PlaygroundConsole(
|
const playgroundConsole = new PlaygroundConsole(
|
||||||
document.querySelector("#console-code"),
|
document.querySelector("#console-code"),
|
||||||
@ -38,11 +40,13 @@ editor.session.setMode("ace/mode/javascript");
|
|||||||
|
|
||||||
editor.setValue(sessionStorage.getItem("code") ?? editor.getValue(), -1);
|
editor.setValue(sessionStorage.getItem("code") ?? editor.getValue(), -1);
|
||||||
|
|
||||||
|
const importButton = document.querySelector("#import-button");
|
||||||
const runButton = document.querySelector("#run-button");
|
const runButton = document.querySelector("#run-button");
|
||||||
const saveButton = document.querySelector("#save-button");
|
const saveButton = document.querySelector("#save-button");
|
||||||
const saveDropdown = document.querySelector("#save-dropdown");
|
const saveDropdown = document.querySelector("#save-dropdown");
|
||||||
const saveJsButton = document.querySelector("#save-js");
|
const saveJsButton = document.querySelector("#save-js");
|
||||||
const saveHtmlButton = document.querySelector("#save-html");
|
const saveHtmlButton = document.querySelector("#save-html");
|
||||||
|
const saveKarlkoderButton = document.querySelector("#save-karlkoder");
|
||||||
const toggleSpriteEditorButton = document.querySelector("#toggle-sprite-editor-button");
|
const toggleSpriteEditorButton = document.querySelector("#toggle-sprite-editor-button");
|
||||||
|
|
||||||
const sessionSaveDebounce = new Debounce(1000);
|
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) => {
|
runButton.onclick = (ev) => {
|
||||||
const code = editor.getValue();
|
const code = editor.getValue();
|
||||||
|
|
||||||
@ -148,6 +163,19 @@ ${js}
|
|||||||
downloadFile(html, "text/html", ".html");
|
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", () => {
|
toggleSpriteEditorButton.addEventListener("click", () => {
|
||||||
const cont = document.querySelector("#sprite-editor-container");
|
const cont = document.querySelector("#sprite-editor-container");
|
||||||
if (cont.style.getPropertyValue("display") === "none") {
|
if (cont.style.getPropertyValue("display") === "none") {
|
||||||
|
19
src/prompt_upload.js
Normal file
19
src/prompt_upload.js
Normal file
@ -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();
|
||||||
|
});
|
||||||
|
}
|
@ -1,3 +1,5 @@
|
|||||||
|
import { promptUpload } from "./prompt_upload.js";
|
||||||
|
|
||||||
export class SpriteEditor {
|
export class SpriteEditor {
|
||||||
constructor(rootEl, sprites) {
|
constructor(rootEl, sprites) {
|
||||||
this.list = rootEl.querySelector("#sprite-editor-sprite-list");
|
this.list = rootEl.querySelector("#sprite-editor-sprite-list");
|
||||||
@ -13,39 +15,26 @@ export class SpriteEditor {
|
|||||||
this.renderList();
|
this.renderList();
|
||||||
}
|
}
|
||||||
|
|
||||||
promptUpload() {
|
async promptUpload() {
|
||||||
const input = document.createElement("input");
|
for (const file of await promptUpload("image/*", true)) {
|
||||||
input.type = "file";
|
const rootName = file.name;
|
||||||
input.accept = "image/*";
|
let fullName = file.name;
|
||||||
input.multiple = true;
|
for (let i = 0; this.sprites.some((x) => x.name === fullName); ++i) {
|
||||||
input.style = "display: none;";
|
const extensionIdx = rootName.split("").findLastIndex((x) => x === ".");
|
||||||
input.addEventListener("cancel", () => {
|
let name = rootName;
|
||||||
input.remove();
|
let extension = "";
|
||||||
});
|
if (extensionIdx !== -1) {
|
||||||
input.addEventListener("change", async () => {
|
name = rootName.slice(0, extensionIdx);
|
||||||
for (const file of input.files) {
|
extension = rootName.slice(extensionIdx);
|
||||||
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}`;
|
|
||||||
}
|
}
|
||||||
this.addSprite({
|
fullName = `${name}-${i}${extension}`;
|
||||||
name: fullName,
|
|
||||||
mime: file.type,
|
|
||||||
bytes: await fetch(URL.createObjectURL(file)).then((x) => x.bytes()),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
input.remove();
|
this.addSprite({
|
||||||
});
|
name: fullName,
|
||||||
document.body.append(input);
|
mime: file.type,
|
||||||
input.click();
|
bytes: await fetch(URL.createObjectURL(file)).then((x) => x.bytes()),
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addSprite({ name, bytes, mime }) {
|
addSprite({ name, bytes, mime }) {
|
||||||
|
@ -2,43 +2,30 @@ const KEYWORDS = ["asset", "code"];
|
|||||||
const MIN_KW_LENGTH = Math.min(...KEYWORDS.map((x) => x.length));
|
const MIN_KW_LENGTH = Math.min(...KEYWORDS.map((x) => x.length));
|
||||||
const SEMICOLON_CHARCODE = ";".charCodeAt(0);
|
const SEMICOLON_CHARCODE = ";".charCodeAt(0);
|
||||||
|
|
||||||
function isKeyword(buffer: number[]) {
|
function isKeyword(buffer) {
|
||||||
const kw = String.fromCharCode(...buffer);
|
const kw = buffer.map((x) => String.fromCharCode(x)).join("");
|
||||||
return KEYWORDS.includes(kw);
|
return KEYWORDS.includes(kw);
|
||||||
}
|
}
|
||||||
|
|
||||||
function strToBytes(str: string): number[] {
|
function strToBytes(str) {
|
||||||
return str.split("").map((x) => x.charCodeAt(0));
|
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 {
|
export class Vermiparous {
|
||||||
private constructor() {}
|
static en(code, assets) {
|
||||||
static en(code: string, assets: EncAsset[]): Uint8Array {
|
const ret = [];
|
||||||
const ret: number[] = [];
|
|
||||||
for (const asset of assets) {
|
for (const asset of assets) {
|
||||||
|
console.log(asset);
|
||||||
ret.push(...strToBytes("asset"));
|
ret.push(...strToBytes("asset"));
|
||||||
ret.push(...strToBytes(asset.name.length.toString()));
|
ret.push(...strToBytes(asset.name.length.toString()));
|
||||||
ret.push(...strToBytes(";"));
|
ret.push(...strToBytes(";"));
|
||||||
ret.push(...strToBytes(asset.mime.length.toString()));
|
ret.push(...strToBytes(asset.mime.length.toString()));
|
||||||
ret.push(...strToBytes(";"));
|
ret.push(...strToBytes(";"));
|
||||||
ret.push(...strToBytes(asset.content.length.toString()));
|
ret.push(...strToBytes(asset.bytes.length.toString()));
|
||||||
ret.push(...strToBytes(";"));
|
ret.push(...strToBytes(";"));
|
||||||
ret.push(...strToBytes(asset.name));
|
ret.push(...strToBytes(asset.name));
|
||||||
ret.push(...strToBytes(asset.mime));
|
ret.push(...strToBytes(asset.mime));
|
||||||
ret.push(...asset.content);
|
ret.push(...asset.bytes);
|
||||||
}
|
}
|
||||||
ret.push(...strToBytes("code"));
|
ret.push(...strToBytes("code"));
|
||||||
ret.push(...strToBytes("0;0;"));
|
ret.push(...strToBytes("0;0;"));
|
||||||
@ -48,7 +35,7 @@ export class Vermiparous {
|
|||||||
return new Uint8Array(ret);
|
return new Uint8Array(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
static de(bytes: Uint8Array): DecAssic[] {
|
static de(bytes) {
|
||||||
const ret = [];
|
const ret = [];
|
||||||
let buffer = [];
|
let buffer = [];
|
||||||
let idx = 0;
|
let idx = 0;
|
||||||
@ -92,7 +79,7 @@ export class Vermiparous {
|
|||||||
}
|
}
|
||||||
const [name, mime, content] = consoom;
|
const [name, mime, content] = consoom;
|
||||||
ret.push({
|
ret.push({
|
||||||
tag: String.fromCharCode(...tag) as DecAssic["tag"],
|
tag: String.fromCharCode(...tag),
|
||||||
name: String.fromCharCode(...name),
|
name: String.fromCharCode(...name),
|
||||||
mime: String.fromCharCode(...mime),
|
mime: String.fromCharCode(...mime),
|
||||||
content: new Uint8Array(content),
|
content: new Uint8Array(content),
|
Loading…
x
Reference in New Issue
Block a user