PageSourceSearch

https://landing.llmosaic.ai/assets/js/ui_component.js

js llmosaic.ai collected 2026-10-05 15:38:05 UTC 4,956 bytes, 142 lines download raw bytes

1// File: docs/assets/js/ui_component.js
2
3;(function() {
4  console.log("📡 ui_component.js loaded");
5
6  // Registry of widget-mounting functions
7  var registry = {
8    select: mountSelect
9  };
10
11  document.addEventListener("DOMContentLoaded", function() {
12    document.querySelectorAll("[data-ui-component]").forEach(function(el) {
13      var cfg;
14      try {
15        cfg = JSON.parse(el.getAttribute("data-ui-component"));
16      } catch (err) {
17        console.error(
18          "[ui_component] JSON parse failed:",
19          err,
20          el.getAttribute("data-ui-component")
21        );
22        return;
23      }
24      var mount = registry[cfg.type];
25      if (!mount) {
26        console.error("[ui_component] unknown widget type:", cfg.type);
27        return;
28      }
29      mount(el, cfg);
30    });
31  });
32
33  function mountSelect(container, cfg) {
34    console.log("[ui_component:" + cfg.id + "] mounting select");
35
36    // 1) build label + <select>
37    var label = document.createElement("label");
38    label.htmlFor = cfg.id;
39    label.textContent = cfg.label || "";
40    // Apply Material theme label class
41    label.classList.add("md-label");
42    // Ensure consistent font-size and spacing
43    label.classList.add("md-body-1");
44    label.style.marginRight = "0.5rem";
45
46    var select = document.createElement("select");
47    select.id = cfg.id;
48    // Apply Material theme input/select class
49    select.classList.add("md-input");
50    // Ensure consistent font-size and spacing
51    select.classList.add("md-body-1");
52    select.style.marginLeft = "0.5rem";
53    // Match font-size to label
54    select.style.fontSize = window.getComputedStyle(label).fontSize;
55
56    // 2) swap out placeholder <div> for our real UI
57    var wrapper = document.createElement("div");
58    // Apply Material theme form-field wrapper class
59    wrapper.classList.add("md-form-field");
60    // Add vertical spacing below each label/select pair
61    wrapper.style.marginBottom = "1rem";
62    wrapper.appendChild(label);
63    wrapper.appendChild(select);
64    container.replaceWith(wrapper);
65
66    // 3) derive the root state key (e.g. "accessKeys" or "selectedKey")
67    var dataKey = cfg.options.split(/[^A-Za-z0-9_]/)[0];
68
69    // 4) build an options-getter that bails if the root value is null/undefined
70    //    and uses StateStore.get(...) instead of reading S.state directly.
71    var optSrc =
72      "try {\n" +
73      "  var root = S.get('" + dataKey + "');\n" +
74      "  if (!root) return [];\n" +
75      "  with ({ " + dataKey + ": root }) { return (" + cfg.options + ") || []; }\n" +
76      "} catch (e) {\n" +
77      "  console.warn('[ui_component] options failed:', " +
78                  JSON.stringify(cfg.options) + ", e);\n" +
79      "  return [];\n" +
80      "}";
81    var getOptions = new Function("S", optSrc);
82
83    // 5) value and label extractors
84    var getVal = cfg.valueField === "this"
85      ? function(item) { return item; }
86      : new Function("item", "return item." + cfg.valueField + ";");
87
88    var getLab = new Function("item",
89      "try { with(item) { return " + cfg.labelExpr + "; } } " +
90      "catch(e) { console.warn('[ui_component] labelExpr failed:', " +
91                  JSON.stringify(cfg.labelExpr) + ", e); return ''; }"
92    );
93
94    // 6) render function with auto-default
95    function render() {
96      try {
97        var opts = getOptions(window.StateStore);
98        console.debug(
99          "[ui_component:" + cfg.id + "] rendering " + opts.length + " options"
100        );
101        select.innerHTML = opts.map(function(o) {
102          var v = getVal(o);
103          var l = getLab(o);
104          return '<option value="' + v + '">' + l + '</option>';
105        }).join("");
106
107        var cur = window.StateStore.get(cfg.selected);
108
109        // If nothing selected yet (null OR empty string), default to first option
110        if ((cur == null || cur === "") && opts.length > 0) {
111          console.debug("[ui_component:" + cfg.id + "] defaulting to first option");
112          window.StateStore.set(cfg.onChange, opts[0]);
113        }
114        // Otherwise reflect the current selection
115        else if (cur != null && cur !== "") {
116          select.value = String(getVal(cur));
117        }
118      } catch (err) {
119        console.error("[ui_component:" + cfg.id + "] render error:", err);
120      }
121    }
122
123    // 7) initial render + subscriptions
124    render();
125    window.StateStore.subscribe(dataKey,     render);
126    window.StateStore.subscribe(cfg.selected, render);
127
128    // 8) wire up user changes
129    select.addEventListener("change", function() {
130      try {
131        var opts   = getOptions(window.StateStore);
132        var picked = opts.find(function(o) {
133          return String(getVal(o)) === select.value;
134        });
135        console.debug("[ui_component:" + cfg.id + "] user selected:", picked);
136        window.StateStore.set(cfg.onChange, picked);
137      } catch (err) {
138        console.error("[ui_component:" + cfg.id + "] onChange error:", err);
139      }
140    });
141  }
142})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.