// ── On-demand library loader ──────────────────────────────────────────────── // // Fetches procedure definitions from JSON files in the libraries/ directory. // At startup only the index is loaded; individual libraries are fetched when // the user clicks their tab for the first time. // // Depends on: app.js (addLibTab, deriveNaturalName) // blocks-proc.js (WCProcedure) // wcprogram.js (BLOCK_REGISTRY) /** @type {Set} Tracks which libraries have been loaded (prevents re-loading). */ const LOADED_LIBS = new Set(); /** @type {Array<{id:string, name:string, group?:string}>} */ let LIB_INDEX = []; /** * Creates param rows on a procedure block and sets their name/type values. * Follows the same pattern as WCProcedure.restoreData(). * @param {HTMLElement} proc A wc-procedure element * @param {Array<[string,string]>} inputs Array of [name, type] pairs * @param {Array<[string,string]>} outputs Array of [name, type] pairs */ function _addProcParams(proc, inputs, outputs) { for (const [name, type] of inputs) { proc._addParamRow(proc.querySelector(".proc-inputs"), "in"); const rows = proc.querySelectorAll(".proc-inputs .proc-param-row"); const last = rows[rows.length - 1]; last.querySelector(".proc-param-name").value = name; last.querySelector(".proc-param-type").value = type; } for (const [name, type] of outputs) { proc._addParamRow(proc.querySelector(".proc-outputs"), "out"); const rows = proc.querySelectorAll(".proc-outputs .proc-param-row"); const last = rows[rows.length - 1]; last.querySelector(".proc-param-name").value = name; last.querySelector(".proc-param-type").value = type; } } /** * Fetches libraries/index.json and returns the library metadata array. * Called once at startup. * @returns {Promise>} */ async function loadLibraryIndex() { try { const res = await fetch("libraries/index.json"); if (!res.ok) throw new Error(`HTTP ${res.status}`); LIB_INDEX = await res.json(); } catch (e) { showError("Could not load library index: " + e.message); LIB_INDEX = []; } return LIB_INDEX; } /** * Loads a single library by its id (e.g. "stdio", "sdl3"). * Creates the tab if it doesn't exist yet, then populates all procedures. * Idempotent — subsequent calls for the same lib are no-ops. */ async function loadLibrary(libId) { if (LOADED_LIBS.has(libId)) return; LOADED_LIBS.add(libId); const prog = $id("main-program"); if (!prog) return; let data; try { const res = await fetch(`libraries/${libId}.json`); if (!res.ok) throw new Error(`HTTP ${res.status}`); data = await res.json(); } catch (e) { showError(`Could not load library "${libId}": ${e.message}`); return; } const procs = data.procedures; if (!procs || !procs.length) return; /* Mark this lib as the active tab if it's the first one loaded */ const isFirst = LOADED_LIBS.size === 1; /* Ensure the tab exists — but don't duplicate if already created from index */ if (!document.querySelector(`.lib-tab[data-lib-name="${libId}"]`)) { addLibTab(libId, isFirst); } for (const pd of procs) { const proc = prog.newProcedureNode(libId); if (!proc) continue; proc.querySelector(".proc-tech-name").textContent = pd.name; proc.querySelector(".proc-natural-name").value = pd.naturalName || deriveNaturalName(pd.name); if (pd.desc) { const db = proc.querySelector(".proc-desc-body"); if (db) db.textContent = pd.desc; } proc.dataset.readonly = "true"; if (pd.returnStyle === "value") proc.dataset.returnStyle = "value"; if (pd.returnStyle === "error") proc.dataset.returnStyle = "error"; if (pd.formatString) proc.dataset.formatString = "true"; _addProcParams(proc, pd.inputs || [], pd.outputs || []); } } /** * Returns the loaded library index (synchronous after loadLibraryIndex). * @returns {Array<{id:string, name:string, group?:string}>} */ function getLibraryIndex() { return LIB_INDEX; } /** * Checks if a library has been loaded yet. * @param {string} libId * @returns {boolean} */ function isLibraryLoaded(libId) { return LOADED_LIBS.has(libId); } /** * Marks a library as loaded (for user-created libs that don't have JSON files). * @param {string} libId */ function markLibLoaded(libId) { LOADED_LIBS.add(libId); }