import { world, } from "./index.js"; import { available_tiles, } from "/stackline-wasm/pkg/stackline_wasm.js"; const right_pane = document.getElementById("right-pane"); const coordinates_elem = document.getElementById("coordinates"); const stack_elem = document.getElementById("stack"); const signal_elem = document.getElementById("signal"); const state_elem = document.getElementById("state"); const tile_elem = document.getElementById("tile"); const tile_name_elem = document.getElementById("tile-name"); const properties_elem = document.getElementById("tile-properties"); let templates = new Map(); export function update_selected(x, y) { coordinates.innerText = `(${x}, ${y})`; let full_tile = world.get(x, y); if (!full_tile) { right_pane.classList.remove("selected"); return; } let signal = full_tile.signal; while (stack_elem.children.length > 0) { stack_elem.removeChild(stack_elem.firstChild); } if (signal) { signal_elem.classList.add("has-signal"); for (let element of signal.stack) { let li = document.createElement("li"); if (typeof element === "string") { li.innerText = `"${element}"`; } else if (typeof element === "number") { li.innerText = element.toString(); } else { throw new Error("Unexpected element type: " + typeof element); } stack_elem.appendChild(li); } signal.free(); } else { signal_elem.classList.remove("has-signal"); } let tile = full_tile.tile; if (tile) { tile_elem.classList.add("has-tile"); let name = Object.keys(tile)[0]; tile_name_elem.innerText = name; // Set properties // TODO: only change the properties when necessary while (properties_elem.children.length > 0) { properties_elem.removeChild(properties_elem.firstChild); } let schema = full_tile.schema(); for (let [label, type, value] of schema_iter(schema, tile[name])) { let input = create_input(type, value, on_change); if (!input) continue; let elem = document.createElement("li"); let label_elem = document.createElement("b"); label_elem.innerText = `${label}: `; elem.appendChild(label_elem); elem.appendChild(input); properties_elem.appendChild(elem); function on_change(new_value) { // Traverse `tile` and change the corresponding schema element tile[name] = schema_recurse(schema, tile[name], label, new_value); let full_tile = world.get(x, y); full_tile.tile = tile; world.set(x, y, full_tile); } } } else { tile_elem.classList.remove("has-tile"); } state_elem.innerText = full_tile.state; full_tile.free(); right_pane.classList.add("selected"); } export function init() { // for (let name of available_tiles()) { // let option = document.createElement("option"); // option.innerText = name; // option.value = name; // tile_name_elem.appendChild(option); // } document.querySelectorAll(".property-template").forEach((template) => { templates.set(template.attributes?.name?.value ?? template.id, template); }); } function* schema_iter(schema, tile) { if (Array.isArray(schema)) { for (let n = 0; n < schema.length; n++) { yield* schema_iter(schema[n], tile[n]); } } else if (typeof schema === "object") { for (let name in schema) { yield* schema_iter(schema[name], tile[name]); } } else if (typeof schema === "string") { let res = schema.split(":"); res.push(tile); yield res; } } function schema_recurse(schema, tile, label, value) { if (Array.isArray(schema)) { for (let n = 0; n < schema.length; n++) { tile[n] = schema_recurse(schema[n], tile[n], label, value); } return tile; } else if (typeof schema === "object") { for (let name in schema) { tile[name] = schema_recurse(schema[name], tile[name], label, value); } return tile; } else if (typeof schema === "string") { if (schema.split(":")[0] === label) { return value; } else { return tile; } } } function create_input(type, value, change_cb) { let options = type.split("|"); for (let option of options) { if (option === "Value") { let res = templates.get("Value").content.cloneNode(true).querySelector("*"); let select_elem = res.querySelector("select"); let type = Object.keys(value)[0]; set_type(type); function set_type(new_type) { let map = new Map([["String", "string"], ["Number", "number"]]); for (let [ty, cn] of map) { if (ty === new_type) { res.classList.add(cn); } else { res.classList.remove(cn); } } type = new_type; } select_elem.addEventListener("change", () => { set_type(select_elem.value); if (type === "Number") { value = {"Number": 0.0}; number_input.value = 0; } else if (type === "String") { value = {"String": ""}; string_input.value = ""; } change_cb(value); }); let number_input = res.querySelector(".value-number input"); if (type === "Number") { number_input.value = value["Number"]; } number_input.addEventListener("change", (evt) => { if (type === "Number") { value["Number"] = +number_input.value; change_cb(value); } }); let string_input = res.querySelector(".value-string input"); if (type === "String") { string_input.value = value["String"]; } string_input.addEventListener("change", (evt) => { if (type === "String") { value["String"] = string_input.value; change_cb(value); } }); return res; } else if (option === "Uint" || option === "Int") { let res = templates.get(option).content.cloneNode(true).querySelector("*"); if (value !== undefined) { res.value = value; } res.addEventListener("change", () => { change_cb(+res.value); }); return res; } else if (option === "Signal") { let res = templates.get(option).content.cloneNode(true).querySelector("*"); let stack_elem = res.querySelector(".stack"); for (let elem of value.stack) { let li = document.createElement("li"); if (elem["Number"]) { li.innerText = elem["Number"]; } else if (elem["String"]) { li.innerText = `"${elem["String"]}"`; } stack_elem.appendChild(li); } let direction_elem = res.querySelector(".direction"); direction_elem.value = value.direction; return res; } if (templates.has(option)) { let res = templates.get(option).content.cloneNode(true).querySelector("*"); if (value !== undefined) { res.value = value; } res.addEventListener("change", () => { change_cb(res.value); }); return res; } } }