import createLinuxCncIniModule from "./linuxcnc_ini.js"; const SAMPLE_PATH = "../../../linuxcnc/configs/sim/axis/vismach/5axis/table-dual-rotary/xyzab-tdr.ini"; const OPFS_FILE = "linuxcnc/xyzab-tdr.ini"; const WASM_FILE = "/work/xyzab-tdr.ini"; const editor = document.getElementById("ini-editor"); const logNode = document.getElementById("log"); const wasmBadge = document.getElementById("wasm-badge"); const opfsBadge = document.getElementById("opfs-badge"); const fields = { machine: document.getElementById("field-machine"), display: document.getElementById("field-display"), kinematics: document.getElementById("field-kinematics"), joints: document.getElementById("field-joints"), coordinates: document.getElementById("field-coordinates"), parameterFile: document.getElementById("field-parameter-file"), }; let moduleInstance = null; function setBadge(node, text, className = "badge") { node.className = className; node.textContent = text; } function setLog(message, isError = false) { logNode.textContent = message; logNode.className = `log${isError ? " danger" : ""}`; } function setField(name, value) { fields[name].textContent = value ?? "-"; } function requireModule() { if (!moduleInstance) { throw new Error("WASM module is not ready yet."); } return moduleInstance; } function allocCString(mod, value) { const bytes = mod.lengthBytesUTF8(value) + 1; const ptr = mod._malloc(bytes); mod.stringToUTF8(value, ptr, bytes); return ptr; } function iniQuery(mod, wasmPath, section, tag) { const pathPtr = allocCString(mod, wasmPath); const sectionPtr = allocCString(mod, section); const tagPtr = allocCString(mod, tag); const outSize = 2048; const outPtr = mod._malloc(outSize); try { const rc = mod._lcini_get_string(pathPtr, sectionPtr, tagPtr, outPtr, outSize); if (rc !== 0) { return null; } return mod.UTF8ToString(outPtr); } finally { mod._free(pathPtr); mod._free(sectionPtr); mod._free(tagPtr); mod._free(outPtr); } } async function getOpfsRoot() { if (!navigator.storage?.getDirectory) { throw new Error("OPFS is not available in this browser."); } return navigator.storage.getDirectory(); } async function ensureParentDir(root, path) { const parts = path.split("/"); let current = root; for (const part of parts.slice(0, -1)) { current = await current.getDirectoryHandle(part, { create: true }); } return current; } async function saveToOpfs(path, text) { const root = await getOpfsRoot(); const dir = await ensureParentDir(root, path); const filename = path.split("/").at(-1); const fileHandle = await dir.getFileHandle(filename, { create: true }); const writable = await fileHandle.createWritable(); await writable.write(text); await writable.close(); } async function loadFromOpfs(path) { const root = await getOpfsRoot(); const parts = path.split("/"); let current = root; for (const part of parts.slice(0, -1)) { current = await current.getDirectoryHandle(part); } const fileHandle = await current.getFileHandle(parts.at(-1)); const file = await fileHandle.getFile(); return file.text(); } function syncEditorToWasmFs() { const mod = requireModule(); try { mod.FS.mkdir("/work"); } catch { // already exists } mod.FS.writeFile(WASM_FILE, editor.value, { encoding: "utf8" }); } async function loadSample() { setLog(`Fetching ${SAMPLE_PATH}`); const response = await fetch(SAMPLE_PATH); if (!response.ok) { throw new Error(`Cannot fetch sample INI (${response.status})`); } editor.value = await response.text(); setLog("Loaded LinuxCNC sample INI into the standalone editor."); } async function boot() { try { moduleInstance = await createLinuxCncIniModule(); setBadge(wasmBadge, "WASM: ready"); } catch (error) { setBadge(wasmBadge, "WASM: failed", "badge danger"); setLog(`WASM init failed: ${error.message}`, true); throw error; } try { await getOpfsRoot(); setBadge(opfsBadge, "OPFS: ready"); } catch (error) { setBadge(opfsBadge, "OPFS: unavailable", "badge danger"); setLog(`OPFS check failed: ${error.message}`, true); } } document.getElementById("load-sample").addEventListener("click", async () => { try { await loadSample(); } catch (error) { setLog(error.message, true); } }); document.getElementById("save-opfs").addEventListener("click", async () => { try { await saveToOpfs(OPFS_FILE, editor.value); setLog(`Saved current INI text to OPFS at ${OPFS_FILE}`); } catch (error) { setLog(`OPFS save failed: ${error.message}`, true); } }); document.getElementById("load-opfs").addEventListener("click", async () => { try { editor.value = await loadFromOpfs(OPFS_FILE); setLog(`Loaded INI text from OPFS path ${OPFS_FILE}`); } catch (error) { setLog(`OPFS load failed: ${error.message}`, true); } }); document.getElementById("sync-wasm").addEventListener("click", () => { try { syncEditorToWasmFs(); setLog(`Synced editor contents to WASM filesystem at ${WASM_FILE}`); } catch (error) { setLog(`WASM sync failed: ${error.message}`, true); } }); document.getElementById("query").addEventListener("click", async () => { try { syncEditorToWasmFs(); const mod = requireModule(); setField("machine", iniQuery(mod, WASM_FILE, "EMC", "MACHINE")); setField("display", iniQuery(mod, WASM_FILE, "DISPLAY", "DISPLAY")); setField("kinematics", iniQuery(mod, WASM_FILE, "KINS", "KINEMATICS")); setField("joints", iniQuery(mod, WASM_FILE, "KINS", "JOINTS")); setField("coordinates", iniQuery(mod, WASM_FILE, "TRAJ", "COORDINATES")); setField( "parameterFile", iniQuery(mod, WASM_FILE, "RS274NGC", "PARAMETER_FILE"), ); setLog("Queried LinuxCNC INI fields through the standalone WASM parser."); } catch (error) { setLog(`Query failed: ${error.message}`, true); } }); boot().catch(() => {});