diff --git a/pkg/api/ui/dist/.gitkeep b/pkg/api/ui/dist/.gitkeep
deleted file mode 100644
index e69de29..0000000
diff --git a/pkg/api/ui/dist/core-scm.js b/pkg/api/ui/dist/core-scm.js
new file mode 100644
index 0000000..df348a3
--- /dev/null
+++ b/pkg/api/ui/dist/core-scm.js
@@ -0,0 +1,2007 @@
+/**
+ * @license
+ * Copyright 2019 Google LLC
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+const J = globalThis, ie = J.ShadowRoot && (J.ShadyCSS === void 0 || J.ShadyCSS.nativeShadow) && "adoptedStyleSheets" in Document.prototype && "replace" in CSSStyleSheet.prototype, re = Symbol(), ne = /* @__PURE__ */ new WeakMap();
+let ye = class {
+ constructor(e, s, r) {
+ if (this._$cssResult$ = !0, r !== re) throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");
+ this.cssText = e, this.t = s;
+ }
+ get styleSheet() {
+ let e = this.o;
+ const s = this.t;
+ if (ie && e === void 0) {
+ const r = s !== void 0 && s.length === 1;
+ r && (e = ne.get(s)), e === void 0 && ((this.o = e = new CSSStyleSheet()).replaceSync(this.cssText), r && ne.set(s, e));
+ }
+ return e;
+ }
+ toString() {
+ return this.cssText;
+ }
+};
+const xe = (t) => new ye(typeof t == "string" ? t : t + "", void 0, re), W = (t, ...e) => {
+ const s = t.length === 1 ? t[0] : e.reduce((r, i, o) => r + ((a) => {
+ if (a._$cssResult$ === !0) return a.cssText;
+ if (typeof a == "number") return a;
+ throw Error("Value passed to 'css' function must be a 'css' function result: " + a + ". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.");
+ })(i) + t[o + 1], t[0]);
+ return new ye(s, t, re);
+}, Se = (t, e) => {
+ if (ie) t.adoptedStyleSheets = e.map((s) => s instanceof CSSStyleSheet ? s : s.styleSheet);
+ else for (const s of e) {
+ const r = document.createElement("style"), i = J.litNonce;
+ i !== void 0 && r.setAttribute("nonce", i), r.textContent = s.cssText, t.appendChild(r);
+ }
+}, le = ie ? (t) => t : (t) => t instanceof CSSStyleSheet ? ((e) => {
+ let s = "";
+ for (const r of e.cssRules) s += r.cssText;
+ return xe(s);
+})(t) : t;
+/**
+ * @license
+ * Copyright 2017 Google LLC
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+const { is: Ee, defineProperty: Ce, getOwnPropertyDescriptor: ke, getOwnPropertyNames: Pe, getOwnPropertySymbols: Ue, getPrototypeOf: Oe } = Object, A = globalThis, de = A.trustedTypes, Re = de ? de.emptyScript : "", Y = A.reactiveElementPolyfillSupport, D = (t, e) => t, Q = { toAttribute(t, e) {
+ switch (e) {
+ case Boolean:
+ t = t ? Re : null;
+ break;
+ case Object:
+ case Array:
+ t = t == null ? t : JSON.stringify(t);
+ }
+ return t;
+}, fromAttribute(t, e) {
+ let s = t;
+ switch (e) {
+ case Boolean:
+ s = t !== null;
+ break;
+ case Number:
+ s = t === null ? null : Number(t);
+ break;
+ case Object:
+ case Array:
+ try {
+ s = JSON.parse(t);
+ } catch {
+ s = null;
+ }
+ }
+ return s;
+} }, ae = (t, e) => !Ee(t, e), ce = { attribute: !0, type: String, converter: Q, reflect: !1, useDefault: !1, hasChanged: ae };
+Symbol.metadata ?? (Symbol.metadata = Symbol("metadata")), A.litPropertyMetadata ?? (A.litPropertyMetadata = /* @__PURE__ */ new WeakMap());
+let R = class extends HTMLElement {
+ static addInitializer(e) {
+ this._$Ei(), (this.l ?? (this.l = [])).push(e);
+ }
+ static get observedAttributes() {
+ return this.finalize(), this._$Eh && [...this._$Eh.keys()];
+ }
+ static createProperty(e, s = ce) {
+ if (s.state && (s.attribute = !1), this._$Ei(), this.prototype.hasOwnProperty(e) && ((s = Object.create(s)).wrapped = !0), this.elementProperties.set(e, s), !s.noAccessor) {
+ const r = Symbol(), i = this.getPropertyDescriptor(e, r, s);
+ i !== void 0 && Ce(this.prototype, e, i);
+ }
+ }
+ static getPropertyDescriptor(e, s, r) {
+ const { get: i, set: o } = ke(this.prototype, e) ?? { get() {
+ return this[s];
+ }, set(a) {
+ this[s] = a;
+ } };
+ return { get: i, set(a) {
+ const d = i == null ? void 0 : i.call(this);
+ o == null || o.call(this, a), this.requestUpdate(e, d, r);
+ }, configurable: !0, enumerable: !0 };
+ }
+ static getPropertyOptions(e) {
+ return this.elementProperties.get(e) ?? ce;
+ }
+ static _$Ei() {
+ if (this.hasOwnProperty(D("elementProperties"))) return;
+ const e = Oe(this);
+ e.finalize(), e.l !== void 0 && (this.l = [...e.l]), this.elementProperties = new Map(e.elementProperties);
+ }
+ static finalize() {
+ if (this.hasOwnProperty(D("finalized"))) return;
+ if (this.finalized = !0, this._$Ei(), this.hasOwnProperty(D("properties"))) {
+ const s = this.properties, r = [...Pe(s), ...Ue(s)];
+ for (const i of r) this.createProperty(i, s[i]);
+ }
+ const e = this[Symbol.metadata];
+ if (e !== null) {
+ const s = litPropertyMetadata.get(e);
+ if (s !== void 0) for (const [r, i] of s) this.elementProperties.set(r, i);
+ }
+ this._$Eh = /* @__PURE__ */ new Map();
+ for (const [s, r] of this.elementProperties) {
+ const i = this._$Eu(s, r);
+ i !== void 0 && this._$Eh.set(i, s);
+ }
+ this.elementStyles = this.finalizeStyles(this.styles);
+ }
+ static finalizeStyles(e) {
+ const s = [];
+ if (Array.isArray(e)) {
+ const r = new Set(e.flat(1 / 0).reverse());
+ for (const i of r) s.unshift(le(i));
+ } else e !== void 0 && s.push(le(e));
+ return s;
+ }
+ static _$Eu(e, s) {
+ const r = s.attribute;
+ return r === !1 ? void 0 : typeof r == "string" ? r : typeof e == "string" ? e.toLowerCase() : void 0;
+ }
+ constructor() {
+ super(), this._$Ep = void 0, this.isUpdatePending = !1, this.hasUpdated = !1, this._$Em = null, this._$Ev();
+ }
+ _$Ev() {
+ var e;
+ this._$ES = new Promise((s) => this.enableUpdating = s), this._$AL = /* @__PURE__ */ new Map(), this._$E_(), this.requestUpdate(), (e = this.constructor.l) == null || e.forEach((s) => s(this));
+ }
+ addController(e) {
+ var s;
+ (this._$EO ?? (this._$EO = /* @__PURE__ */ new Set())).add(e), this.renderRoot !== void 0 && this.isConnected && ((s = e.hostConnected) == null || s.call(e));
+ }
+ removeController(e) {
+ var s;
+ (s = this._$EO) == null || s.delete(e);
+ }
+ _$E_() {
+ const e = /* @__PURE__ */ new Map(), s = this.constructor.elementProperties;
+ for (const r of s.keys()) this.hasOwnProperty(r) && (e.set(r, this[r]), delete this[r]);
+ e.size > 0 && (this._$Ep = e);
+ }
+ createRenderRoot() {
+ const e = this.shadowRoot ?? this.attachShadow(this.constructor.shadowRootOptions);
+ return Se(e, this.constructor.elementStyles), e;
+ }
+ connectedCallback() {
+ var e;
+ this.renderRoot ?? (this.renderRoot = this.createRenderRoot()), this.enableUpdating(!0), (e = this._$EO) == null || e.forEach((s) => {
+ var r;
+ return (r = s.hostConnected) == null ? void 0 : r.call(s);
+ });
+ }
+ enableUpdating(e) {
+ }
+ disconnectedCallback() {
+ var e;
+ (e = this._$EO) == null || e.forEach((s) => {
+ var r;
+ return (r = s.hostDisconnected) == null ? void 0 : r.call(s);
+ });
+ }
+ attributeChangedCallback(e, s, r) {
+ this._$AK(e, r);
+ }
+ _$ET(e, s) {
+ var o;
+ const r = this.constructor.elementProperties.get(e), i = this.constructor._$Eu(e, r);
+ if (i !== void 0 && r.reflect === !0) {
+ const a = (((o = r.converter) == null ? void 0 : o.toAttribute) !== void 0 ? r.converter : Q).toAttribute(s, r.type);
+ this._$Em = e, a == null ? this.removeAttribute(i) : this.setAttribute(i, a), this._$Em = null;
+ }
+ }
+ _$AK(e, s) {
+ var o, a;
+ const r = this.constructor, i = r._$Eh.get(e);
+ if (i !== void 0 && this._$Em !== i) {
+ const d = r.getPropertyOptions(i), n = typeof d.converter == "function" ? { fromAttribute: d.converter } : ((o = d.converter) == null ? void 0 : o.fromAttribute) !== void 0 ? d.converter : Q;
+ this._$Em = i;
+ const u = n.fromAttribute(s, d.type);
+ this[i] = u ?? ((a = this._$Ej) == null ? void 0 : a.get(i)) ?? u, this._$Em = null;
+ }
+ }
+ requestUpdate(e, s, r, i = !1, o) {
+ var a;
+ if (e !== void 0) {
+ const d = this.constructor;
+ if (i === !1 && (o = this[e]), r ?? (r = d.getPropertyOptions(e)), !((r.hasChanged ?? ae)(o, s) || r.useDefault && r.reflect && o === ((a = this._$Ej) == null ? void 0 : a.get(e)) && !this.hasAttribute(d._$Eu(e, r)))) return;
+ this.C(e, s, r);
+ }
+ this.isUpdatePending === !1 && (this._$ES = this._$EP());
+ }
+ C(e, s, { useDefault: r, reflect: i, wrapped: o }, a) {
+ r && !(this._$Ej ?? (this._$Ej = /* @__PURE__ */ new Map())).has(e) && (this._$Ej.set(e, a ?? s ?? this[e]), o !== !0 || a !== void 0) || (this._$AL.has(e) || (this.hasUpdated || r || (s = void 0), this._$AL.set(e, s)), i === !0 && this._$Em !== e && (this._$Eq ?? (this._$Eq = /* @__PURE__ */ new Set())).add(e));
+ }
+ async _$EP() {
+ this.isUpdatePending = !0;
+ try {
+ await this._$ES;
+ } catch (s) {
+ Promise.reject(s);
+ }
+ const e = this.scheduleUpdate();
+ return e != null && await e, !this.isUpdatePending;
+ }
+ scheduleUpdate() {
+ return this.performUpdate();
+ }
+ performUpdate() {
+ var r;
+ if (!this.isUpdatePending) return;
+ if (!this.hasUpdated) {
+ if (this.renderRoot ?? (this.renderRoot = this.createRenderRoot()), this._$Ep) {
+ for (const [o, a] of this._$Ep) this[o] = a;
+ this._$Ep = void 0;
+ }
+ const i = this.constructor.elementProperties;
+ if (i.size > 0) for (const [o, a] of i) {
+ const { wrapped: d } = a, n = this[o];
+ d !== !0 || this._$AL.has(o) || n === void 0 || this.C(o, void 0, a, n);
+ }
+ }
+ let e = !1;
+ const s = this._$AL;
+ try {
+ e = this.shouldUpdate(s), e ? (this.willUpdate(s), (r = this._$EO) == null || r.forEach((i) => {
+ var o;
+ return (o = i.hostUpdate) == null ? void 0 : o.call(i);
+ }), this.update(s)) : this._$EM();
+ } catch (i) {
+ throw e = !1, this._$EM(), i;
+ }
+ e && this._$AE(s);
+ }
+ willUpdate(e) {
+ }
+ _$AE(e) {
+ var s;
+ (s = this._$EO) == null || s.forEach((r) => {
+ var i;
+ return (i = r.hostUpdated) == null ? void 0 : i.call(r);
+ }), this.hasUpdated || (this.hasUpdated = !0, this.firstUpdated(e)), this.updated(e);
+ }
+ _$EM() {
+ this._$AL = /* @__PURE__ */ new Map(), this.isUpdatePending = !1;
+ }
+ get updateComplete() {
+ return this.getUpdateComplete();
+ }
+ getUpdateComplete() {
+ return this._$ES;
+ }
+ shouldUpdate(e) {
+ return !0;
+ }
+ update(e) {
+ this._$Eq && (this._$Eq = this._$Eq.forEach((s) => this._$ET(s, this[s]))), this._$EM();
+ }
+ updated(e) {
+ }
+ firstUpdated(e) {
+ }
+};
+R.elementStyles = [], R.shadowRootOptions = { mode: "open" }, R[D("elementProperties")] = /* @__PURE__ */ new Map(), R[D("finalized")] = /* @__PURE__ */ new Map(), Y == null || Y({ ReactiveElement: R }), (A.reactiveElementVersions ?? (A.reactiveElementVersions = [])).push("2.1.2");
+/**
+ * @license
+ * Copyright 2017 Google LLC
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+const I = globalThis, he = (t) => t, Z = I.trustedTypes, pe = Z ? Z.createPolicy("lit-html", { createHTML: (t) => t }) : void 0, $e = "$lit$", w = `lit$${Math.random().toFixed(9).slice(2)}$`, _e = "?" + w, ze = `<${_e}>`, U = document, j = () => U.createComment(""), L = (t) => t === null || typeof t != "object" && typeof t != "function", oe = Array.isArray, Te = (t) => oe(t) || typeof (t == null ? void 0 : t[Symbol.iterator]) == "function", ee = `[
+\f\r]`, H = /<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g, ue = /-->/g, fe = />/g, C = RegExp(`>|${ee}(?:([^\\s"'>=/]+)(${ee}*=${ee}*(?:[^
+\f\r"'\`<>=]|("|')|))|$)`, "g"), me = /'/g, ge = /"/g, we = /^(?:script|style|textarea|title)$/i, Me = (t) => (e, ...s) => ({ _$litType$: t, strings: e, values: s }), l = Me(1), z = Symbol.for("lit-noChange"), c = Symbol.for("lit-nothing"), be = /* @__PURE__ */ new WeakMap(), k = U.createTreeWalker(U, 129);
+function Ae(t, e) {
+ if (!oe(t) || !t.hasOwnProperty("raw")) throw Error("invalid template strings array");
+ return pe !== void 0 ? pe.createHTML(e) : e;
+}
+const Ne = (t, e) => {
+ const s = t.length - 1, r = [];
+ let i, o = e === 2 ? "" : e === 3 ? "" : "")), r];
+};
+class q {
+ constructor({ strings: e, _$litType$: s }, r) {
+ let i;
+ this.parts = [];
+ let o = 0, a = 0;
+ const d = e.length - 1, n = this.parts, [u, f] = Ne(e, s);
+ if (this.el = q.createElement(u, r), k.currentNode = this.el.content, s === 2 || s === 3) {
+ const h = this.el.content.firstChild;
+ h.replaceWith(...h.childNodes);
+ }
+ for (; (i = k.nextNode()) !== null && n.length < d; ) {
+ if (i.nodeType === 1) {
+ if (i.hasAttributes()) for (const h of i.getAttributeNames()) if (h.endsWith($e)) {
+ const v = f[a++], _ = i.getAttribute(h).split(w), K = /([.?@])?(.*)/.exec(v);
+ n.push({ type: 1, index: o, name: K[2], strings: _, ctor: K[1] === "." ? De : K[1] === "?" ? Ie : K[1] === "@" ? je : G }), i.removeAttribute(h);
+ } else h.startsWith(w) && (n.push({ type: 6, index: o }), i.removeAttribute(h));
+ if (we.test(i.tagName)) {
+ const h = i.textContent.split(w), v = h.length - 1;
+ if (v > 0) {
+ i.textContent = Z ? Z.emptyScript : "";
+ for (let _ = 0; _ < v; _++) i.append(h[_], j()), k.nextNode(), n.push({ type: 2, index: ++o });
+ i.append(h[v], j());
+ }
+ }
+ } else if (i.nodeType === 8) if (i.data === _e) n.push({ type: 2, index: o });
+ else {
+ let h = -1;
+ for (; (h = i.data.indexOf(w, h + 1)) !== -1; ) n.push({ type: 7, index: o }), h += w.length - 1;
+ }
+ o++;
+ }
+ }
+ static createElement(e, s) {
+ const r = U.createElement("template");
+ return r.innerHTML = e, r;
+ }
+}
+function T(t, e, s = t, r) {
+ var a, d;
+ if (e === z) return e;
+ let i = r !== void 0 ? (a = s._$Co) == null ? void 0 : a[r] : s._$Cl;
+ const o = L(e) ? void 0 : e._$litDirective$;
+ return (i == null ? void 0 : i.constructor) !== o && ((d = i == null ? void 0 : i._$AO) == null || d.call(i, !1), o === void 0 ? i = void 0 : (i = new o(t), i._$AT(t, s, r)), r !== void 0 ? (s._$Co ?? (s._$Co = []))[r] = i : s._$Cl = i), i !== void 0 && (e = T(t, i._$AS(t, e.values), i, r)), e;
+}
+class He {
+ constructor(e, s) {
+ this._$AV = [], this._$AN = void 0, this._$AD = e, this._$AM = s;
+ }
+ get parentNode() {
+ return this._$AM.parentNode;
+ }
+ get _$AU() {
+ return this._$AM._$AU;
+ }
+ u(e) {
+ const { el: { content: s }, parts: r } = this._$AD, i = ((e == null ? void 0 : e.creationScope) ?? U).importNode(s, !0);
+ k.currentNode = i;
+ let o = k.nextNode(), a = 0, d = 0, n = r[0];
+ for (; n !== void 0; ) {
+ if (a === n.index) {
+ let u;
+ n.type === 2 ? u = new V(o, o.nextSibling, this, e) : n.type === 1 ? u = new n.ctor(o, n.name, n.strings, this, e) : n.type === 6 && (u = new Le(o, this, e)), this._$AV.push(u), n = r[++d];
+ }
+ a !== (n == null ? void 0 : n.index) && (o = k.nextNode(), a++);
+ }
+ return k.currentNode = U, i;
+ }
+ p(e) {
+ let s = 0;
+ for (const r of this._$AV) r !== void 0 && (r.strings !== void 0 ? (r._$AI(e, r, s), s += r.strings.length - 2) : r._$AI(e[s])), s++;
+ }
+}
+class V {
+ get _$AU() {
+ var e;
+ return ((e = this._$AM) == null ? void 0 : e._$AU) ?? this._$Cv;
+ }
+ constructor(e, s, r, i) {
+ this.type = 2, this._$AH = c, this._$AN = void 0, this._$AA = e, this._$AB = s, this._$AM = r, this.options = i, this._$Cv = (i == null ? void 0 : i.isConnected) ?? !0;
+ }
+ get parentNode() {
+ let e = this._$AA.parentNode;
+ const s = this._$AM;
+ return s !== void 0 && (e == null ? void 0 : e.nodeType) === 11 && (e = s.parentNode), e;
+ }
+ get startNode() {
+ return this._$AA;
+ }
+ get endNode() {
+ return this._$AB;
+ }
+ _$AI(e, s = this) {
+ e = T(this, e, s), L(e) ? e === c || e == null || e === "" ? (this._$AH !== c && this._$AR(), this._$AH = c) : e !== this._$AH && e !== z && this._(e) : e._$litType$ !== void 0 ? this.$(e) : e.nodeType !== void 0 ? this.T(e) : Te(e) ? this.k(e) : this._(e);
+ }
+ O(e) {
+ return this._$AA.parentNode.insertBefore(e, this._$AB);
+ }
+ T(e) {
+ this._$AH !== e && (this._$AR(), this._$AH = this.O(e));
+ }
+ _(e) {
+ this._$AH !== c && L(this._$AH) ? this._$AA.nextSibling.data = e : this.T(U.createTextNode(e)), this._$AH = e;
+ }
+ $(e) {
+ var o;
+ const { values: s, _$litType$: r } = e, i = typeof r == "number" ? this._$AC(e) : (r.el === void 0 && (r.el = q.createElement(Ae(r.h, r.h[0]), this.options)), r);
+ if (((o = this._$AH) == null ? void 0 : o._$AD) === i) this._$AH.p(s);
+ else {
+ const a = new He(i, this), d = a.u(this.options);
+ a.p(s), this.T(d), this._$AH = a;
+ }
+ }
+ _$AC(e) {
+ let s = be.get(e.strings);
+ return s === void 0 && be.set(e.strings, s = new q(e)), s;
+ }
+ k(e) {
+ oe(this._$AH) || (this._$AH = [], this._$AR());
+ const s = this._$AH;
+ let r, i = 0;
+ for (const o of e) i === s.length ? s.push(r = new V(this.O(j()), this.O(j()), this, this.options)) : r = s[i], r._$AI(o), i++;
+ i < s.length && (this._$AR(r && r._$AB.nextSibling, i), s.length = i);
+ }
+ _$AR(e = this._$AA.nextSibling, s) {
+ var r;
+ for ((r = this._$AP) == null ? void 0 : r.call(this, !1, !0, s); e !== this._$AB; ) {
+ const i = he(e).nextSibling;
+ he(e).remove(), e = i;
+ }
+ }
+ setConnected(e) {
+ var s;
+ this._$AM === void 0 && (this._$Cv = e, (s = this._$AP) == null || s.call(this, e));
+ }
+}
+class G {
+ get tagName() {
+ return this.element.tagName;
+ }
+ get _$AU() {
+ return this._$AM._$AU;
+ }
+ constructor(e, s, r, i, o) {
+ this.type = 1, this._$AH = c, this._$AN = void 0, this.element = e, this.name = s, this._$AM = i, this.options = o, r.length > 2 || r[0] !== "" || r[1] !== "" ? (this._$AH = Array(r.length - 1).fill(new String()), this.strings = r) : this._$AH = c;
+ }
+ _$AI(e, s = this, r, i) {
+ const o = this.strings;
+ let a = !1;
+ if (o === void 0) e = T(this, e, s, 0), a = !L(e) || e !== this._$AH && e !== z, a && (this._$AH = e);
+ else {
+ const d = e;
+ let n, u;
+ for (e = o[0], n = 0; n < o.length - 1; n++) u = T(this, d[r + n], s, n), u === z && (u = this._$AH[n]), a || (a = !L(u) || u !== this._$AH[n]), u === c ? e = c : e !== c && (e += (u ?? "") + o[n + 1]), this._$AH[n] = u;
+ }
+ a && !i && this.j(e);
+ }
+ j(e) {
+ e === c ? this.element.removeAttribute(this.name) : this.element.setAttribute(this.name, e ?? "");
+ }
+}
+class De extends G {
+ constructor() {
+ super(...arguments), this.type = 3;
+ }
+ j(e) {
+ this.element[this.name] = e === c ? void 0 : e;
+ }
+}
+class Ie extends G {
+ constructor() {
+ super(...arguments), this.type = 4;
+ }
+ j(e) {
+ this.element.toggleAttribute(this.name, !!e && e !== c);
+ }
+}
+class je extends G {
+ constructor(e, s, r, i, o) {
+ super(e, s, r, i, o), this.type = 5;
+ }
+ _$AI(e, s = this) {
+ if ((e = T(this, e, s, 0) ?? c) === z) return;
+ const r = this._$AH, i = e === c && r !== c || e.capture !== r.capture || e.once !== r.once || e.passive !== r.passive, o = e !== c && (r === c || i);
+ i && this.element.removeEventListener(this.name, this, r), o && this.element.addEventListener(this.name, this, e), this._$AH = e;
+ }
+ handleEvent(e) {
+ var s;
+ typeof this._$AH == "function" ? this._$AH.call(((s = this.options) == null ? void 0 : s.host) ?? this.element, e) : this._$AH.handleEvent(e);
+ }
+}
+class Le {
+ constructor(e, s, r) {
+ this.element = e, this.type = 6, this._$AN = void 0, this._$AM = s, this.options = r;
+ }
+ get _$AU() {
+ return this._$AM._$AU;
+ }
+ _$AI(e) {
+ T(this, e);
+ }
+}
+const te = I.litHtmlPolyfillSupport;
+te == null || te(q, V), (I.litHtmlVersions ?? (I.litHtmlVersions = [])).push("3.3.2");
+const qe = (t, e, s) => {
+ const r = (s == null ? void 0 : s.renderBefore) ?? e;
+ let i = r._$litPart$;
+ if (i === void 0) {
+ const o = (s == null ? void 0 : s.renderBefore) ?? null;
+ r._$litPart$ = i = new V(e.insertBefore(j(), o), o, void 0, s ?? {});
+ }
+ return i._$AI(t), i;
+};
+/**
+ * @license
+ * Copyright 2017 Google LLC
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+const P = globalThis;
+class y extends R {
+ constructor() {
+ super(...arguments), this.renderOptions = { host: this }, this._$Do = void 0;
+ }
+ createRenderRoot() {
+ var s;
+ const e = super.createRenderRoot();
+ return (s = this.renderOptions).renderBefore ?? (s.renderBefore = e.firstChild), e;
+ }
+ update(e) {
+ const s = this.render();
+ this.hasUpdated || (this.renderOptions.isConnected = this.isConnected), super.update(e), this._$Do = qe(s, this.renderRoot, this.renderOptions);
+ }
+ connectedCallback() {
+ var e;
+ super.connectedCallback(), (e = this._$Do) == null || e.setConnected(!0);
+ }
+ disconnectedCallback() {
+ var e;
+ super.disconnectedCallback(), (e = this._$Do) == null || e.setConnected(!1);
+ }
+ render() {
+ return z;
+ }
+}
+var ve;
+y._$litElement$ = !0, y.finalized = !0, (ve = P.litElementHydrateSupport) == null || ve.call(P, { LitElement: y });
+const se = P.litElementPolyfillSupport;
+se == null || se({ LitElement: y });
+(P.litElementVersions ?? (P.litElementVersions = [])).push("4.2.2");
+/**
+ * @license
+ * Copyright 2017 Google LLC
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+const B = (t) => (e, s) => {
+ s !== void 0 ? s.addInitializer(() => {
+ customElements.define(t, e);
+ }) : customElements.define(t, e);
+};
+/**
+ * @license
+ * Copyright 2017 Google LLC
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+const We = { attribute: !0, type: String, converter: Q, reflect: !1, hasChanged: ae }, Ve = (t = We, e, s) => {
+ const { kind: r, metadata: i } = s;
+ let o = globalThis.litPropertyMetadata.get(i);
+ if (o === void 0 && globalThis.litPropertyMetadata.set(i, o = /* @__PURE__ */ new Map()), r === "setter" && ((t = Object.create(t)).wrapped = !0), o.set(s.name, t), r === "accessor") {
+ const { name: a } = s;
+ return { set(d) {
+ const n = e.get.call(this);
+ e.set.call(this, d), this.requestUpdate(a, n, t, !0, d);
+ }, init(d) {
+ return d !== void 0 && this.C(a, void 0, t, d), d;
+ } };
+ }
+ if (r === "setter") {
+ const { name: a } = s;
+ return function(d) {
+ const n = this[a];
+ e.call(this, d), this.requestUpdate(a, n, t, !0, d);
+ };
+ }
+ throw Error("Unsupported decorator location: " + r);
+};
+function $(t) {
+ return (e, s) => typeof s == "object" ? Ve(t, e, s) : ((r, i, o) => {
+ const a = i.hasOwnProperty(o);
+ return i.constructor.createProperty(o, r), a ? Object.getOwnPropertyDescriptor(i, o) : void 0;
+ })(t, e, s);
+}
+/**
+ * @license
+ * Copyright 2017 Google LLC
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+function p(t) {
+ return $({ ...t, state: !0, attribute: !1 });
+}
+function Be(t, e) {
+ const s = new WebSocket(t);
+ return s.onmessage = (r) => {
+ var i, o, a, d;
+ try {
+ const n = JSON.parse(r.data);
+ ((o = (i = n.type) == null ? void 0 : i.startsWith) != null && o.call(i, "scm.") || (d = (a = n.channel) == null ? void 0 : a.startsWith) != null && d.call(a, "scm.")) && e(n);
+ } catch {
+ }
+ }, s;
+}
+class X {
+ constructor(e = "") {
+ this.baseUrl = e;
+ }
+ get base() {
+ return `${this.baseUrl}/api/v1/scm`;
+ }
+ async request(e, s) {
+ var o, a;
+ const r = await fetch(`${this.base}${e}`, s), i = await r.json().catch(() => null);
+ if (!r.ok)
+ throw new Error(((o = i == null ? void 0 : i.error) == null ? void 0 : o.message) ?? `Request failed (${r.status})`);
+ if (!(i != null && i.success))
+ throw new Error(((a = i == null ? void 0 : i.error) == null ? void 0 : a.message) ?? "Request failed");
+ return i.data;
+ }
+ marketplace(e, s) {
+ const r = new URLSearchParams();
+ e && r.set("q", e), s && r.set("category", s);
+ const i = r.toString();
+ return this.request(`/marketplace${i ? `?${i}` : ""}`);
+ }
+ marketplaceItem(e) {
+ return this.request(`/marketplace/${encodeURIComponent(e)}`);
+ }
+ install(e) {
+ return this.request(`/marketplace/${encodeURIComponent(e)}/install`, { method: "POST" });
+ }
+ remove(e) {
+ return this.request(`/marketplace/${encodeURIComponent(e)}`, { method: "DELETE" });
+ }
+ installed() {
+ return this.request("/installed");
+ }
+ updateInstalled(e) {
+ return this.request(`/installed/${encodeURIComponent(e)}/update`, { method: "POST" });
+ }
+ manifest() {
+ return this.request("/manifest");
+ }
+ verify(e) {
+ return this.request("/manifest/verify", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ public_key: e })
+ });
+ }
+ sign(e) {
+ return this.request("/manifest/sign", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ private_key: e })
+ });
+ }
+ permissions() {
+ return this.request("/manifest/permissions");
+ }
+ registry() {
+ return this.request("/registry");
+ }
+}
+var Fe = Object.defineProperty, Ke = Object.getOwnPropertyDescriptor, b = (t, e, s, r) => {
+ for (var i = r > 1 ? void 0 : r ? Ke(e, s) : e, o = t.length - 1, a; o >= 0; o--)
+ (a = t[o]) && (i = (r ? a(e, s, i) : a(i)) || i);
+ return r && i && Fe(e, s, i), i;
+};
+let m = class extends y {
+ constructor() {
+ super(...arguments), this.apiUrl = "", this.category = "", this.modules = [], this.categories = [], this.searchQuery = "", this.activeCategory = "", this.loading = !0, this.error = "", this.installing = /* @__PURE__ */ new Set();
+ }
+ connectedCallback() {
+ super.connectedCallback(), this.api = new X(this.apiUrl), this.activeCategory = this.category, this.loadModules();
+ }
+ async loadModules() {
+ this.loading = !0, this.error = "";
+ try {
+ this.modules = await this.api.marketplace(
+ this.searchQuery || void 0,
+ this.activeCategory || void 0
+ );
+ const t = /* @__PURE__ */ new Set();
+ this.modules.forEach((e) => {
+ e.category && t.add(e.category);
+ }), this.categories = Array.from(t).sort();
+ } catch (t) {
+ this.error = t.message ?? "Failed to load marketplace";
+ } finally {
+ this.loading = !1;
+ }
+ }
+ async refresh() {
+ await this.loadModules();
+ }
+ handleSearch(t) {
+ this.searchQuery = t.target.value, this.loadModules();
+ }
+ handleCategoryClick(t) {
+ this.activeCategory = this.activeCategory === t ? "" : t, this.loadModules();
+ }
+ async handleInstall(t) {
+ this.installing = /* @__PURE__ */ new Set([...this.installing, t]);
+ try {
+ await this.api.install(t), this.dispatchEvent(
+ new CustomEvent("scm-installed", { detail: { code: t }, bubbles: !0 })
+ );
+ } catch (e) {
+ this.error = e.message ?? "Installation failed";
+ } finally {
+ const e = new Set(this.installing);
+ e.delete(t), this.installing = e;
+ }
+ }
+ async handleRemove(t) {
+ try {
+ await this.api.remove(t), this.dispatchEvent(
+ new CustomEvent("scm-removed", { detail: { code: t }, bubbles: !0 })
+ );
+ } catch (e) {
+ this.error = e.message ?? "Removal failed";
+ }
+ }
+ render() {
+ return l`
+
+
+
+
+ ${this.categories.length > 0 ? l`
+
+ ${this.categories.map(
+ (t) => l`
+
+ `
+ )}
+
+ ` : c}
+ ${this.error ? l`${this.error}
` : c}
+ ${this.loading ? l`Loading marketplace\u2026
` : this.modules.length === 0 ? l`No providers found.
` : l`
+
+ ${this.modules.map(
+ (t) => l`
+
+
+
+
+
+
+
+ `
+ )}
+
+ `}
+ `;
+ }
+};
+m.styles = W`
+ :host {
+ display: block;
+ font-family: system-ui, -apple-system, sans-serif;
+ }
+
+ .toolbar {
+ display: flex;
+ gap: 0.5rem;
+ align-items: center;
+ margin-bottom: 1rem;
+ }
+
+ .search {
+ flex: 1;
+ padding: 0.5rem 0.75rem;
+ border: 1px solid #d1d5db;
+ border-radius: 0.375rem;
+ font-size: 0.875rem;
+ outline: none;
+ }
+
+ .search:focus {
+ border-colour: #6366f1;
+ box-shadow: 0 0 0 2px rgba(99, 102, 241, 0.2);
+ }
+
+ .categories {
+ display: flex;
+ gap: 0.25rem;
+ flex-wrap: wrap;
+ }
+
+ .category-btn {
+ padding: 0.25rem 0.75rem;
+ border: 1px solid #e5e7eb;
+ border-radius: 1rem;
+ background: #fff;
+ font-size: 0.75rem;
+ cursor: pointer;
+ transition: all 0.15s;
+ }
+
+ .category-btn:hover {
+ background: #f3f4f6;
+ }
+
+ .category-btn.active {
+ background: #6366f1;
+ colour: #fff;
+ border-colour: #6366f1;
+ }
+
+ .grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
+ gap: 1rem;
+ margin-top: 1rem;
+ }
+
+ .card {
+ border: 1px solid #e5e7eb;
+ border-radius: 0.5rem;
+ padding: 1rem;
+ background: #fff;
+ transition: box-shadow 0.15s;
+ }
+
+ .card:hover {
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
+ }
+
+ .card-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: flex-start;
+ margin-bottom: 0.5rem;
+ }
+
+ .card-name {
+ font-weight: 600;
+ font-size: 0.9375rem;
+ }
+
+ .card-code {
+ font-size: 0.75rem;
+ colour: #6b7280;
+ font-family: monospace;
+ }
+
+ .card-category {
+ font-size: 0.6875rem;
+ padding: 0.125rem 0.5rem;
+ background: #f3f4f6;
+ border-radius: 1rem;
+ colour: #6b7280;
+ }
+
+ .card-actions {
+ margin-top: 0.75rem;
+ display: flex;
+ gap: 0.5rem;
+ }
+
+ button.install {
+ padding: 0.375rem 1rem;
+ background: #6366f1;
+ colour: #fff;
+ border: none;
+ border-radius: 0.375rem;
+ font-size: 0.8125rem;
+ cursor: pointer;
+ transition: background 0.15s;
+ }
+
+ button.install:hover {
+ background: #4f46e5;
+ }
+
+ button.install:disabled {
+ opacity: 0.5;
+ cursor: not-allowed;
+ }
+
+ button.remove {
+ padding: 0.375rem 1rem;
+ background: #fff;
+ colour: #dc2626;
+ border: 1px solid #dc2626;
+ border-radius: 0.375rem;
+ font-size: 0.8125rem;
+ cursor: pointer;
+ }
+
+ button.remove:hover {
+ background: #fef2f2;
+ }
+
+ .empty {
+ text-align: center;
+ padding: 2rem;
+ colour: #9ca3af;
+ font-size: 0.875rem;
+ }
+
+ .loading {
+ text-align: center;
+ padding: 2rem;
+ colour: #6b7280;
+ }
+
+ .error {
+ colour: #dc2626;
+ padding: 0.75rem;
+ background: #fef2f2;
+ border-radius: 0.375rem;
+ font-size: 0.875rem;
+ }
+ `;
+b([
+ $({ attribute: "api-url" })
+], m.prototype, "apiUrl", 2);
+b([
+ $()
+], m.prototype, "category", 2);
+b([
+ p()
+], m.prototype, "modules", 2);
+b([
+ p()
+], m.prototype, "categories", 2);
+b([
+ p()
+], m.prototype, "searchQuery", 2);
+b([
+ p()
+], m.prototype, "activeCategory", 2);
+b([
+ p()
+], m.prototype, "loading", 2);
+b([
+ p()
+], m.prototype, "error", 2);
+b([
+ p()
+], m.prototype, "installing", 2);
+m = b([
+ B("core-scm-marketplace")
+], m);
+var Je = Object.defineProperty, Qe = Object.getOwnPropertyDescriptor, M = (t, e, s, r) => {
+ for (var i = r > 1 ? void 0 : r ? Qe(e, s) : e, o = t.length - 1, a; o >= 0; o--)
+ (a = t[o]) && (i = (r ? a(e, s, i) : a(i)) || i);
+ return r && i && Je(e, s, i), i;
+};
+let x = class extends y {
+ constructor() {
+ super(...arguments), this.apiUrl = "", this.modules = [], this.loading = !0, this.error = "", this.updating = /* @__PURE__ */ new Set();
+ }
+ connectedCallback() {
+ super.connectedCallback(), this.api = new X(this.apiUrl), this.loadInstalled();
+ }
+ async loadInstalled() {
+ this.loading = !0, this.error = "";
+ try {
+ this.modules = await this.api.installed();
+ } catch (t) {
+ this.error = t.message ?? "Failed to load installed providers";
+ } finally {
+ this.loading = !1;
+ }
+ }
+ async refresh() {
+ await this.loadInstalled();
+ }
+ async handleUpdate(t) {
+ this.updating = /* @__PURE__ */ new Set([...this.updating, t]);
+ try {
+ await this.api.updateInstalled(t), await this.loadInstalled();
+ } catch (e) {
+ this.error = e.message ?? "Update failed";
+ } finally {
+ const e = new Set(this.updating);
+ e.delete(t), this.updating = e;
+ }
+ }
+ async handleRemove(t) {
+ try {
+ await this.api.remove(t), this.dispatchEvent(
+ new CustomEvent("scm-removed", { detail: { code: t }, bubbles: !0 })
+ ), await this.loadInstalled();
+ } catch (e) {
+ this.error = e.message ?? "Removal failed";
+ }
+ }
+ formatDate(t) {
+ try {
+ return new Date(t).toLocaleDateString("en-GB", {
+ day: "numeric",
+ month: "short",
+ year: "numeric"
+ });
+ } catch {
+ return t;
+ }
+ }
+ render() {
+ return this.loading ? l`Loading installed providers\u2026
` : l`
+ ${this.error ? l`${this.error}
` : c}
+ ${this.modules.length === 0 ? l`No providers installed.
` : l`
+
+ ${this.modules.map(
+ (t) => l`
+
+
+
${t.name}
+
+ ${t.code}
+ v${t.version}
+ Installed ${this.formatDate(t.installed_at)}
+
+
+
+
+
+
+
+ `
+ )}
+
+ `}
+ `;
+ }
+};
+x.styles = W`
+ :host {
+ display: block;
+ font-family: system-ui, -apple-system, sans-serif;
+ }
+
+ .list {
+ display: flex;
+ flex-direction: column;
+ gap: 0.5rem;
+ }
+
+ .item {
+ border: 1px solid #e5e7eb;
+ border-radius: 0.5rem;
+ padding: 1rem;
+ background: #fff;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ transition: box-shadow 0.15s;
+ }
+
+ .item:hover {
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
+ }
+
+ .item-info {
+ flex: 1;
+ }
+
+ .item-name {
+ font-weight: 600;
+ font-size: 0.9375rem;
+ }
+
+ .item-meta {
+ font-size: 0.75rem;
+ colour: #6b7280;
+ margin-top: 0.25rem;
+ display: flex;
+ gap: 1rem;
+ }
+
+ .item-code {
+ font-family: monospace;
+ }
+
+ .item-actions {
+ display: flex;
+ gap: 0.5rem;
+ }
+
+ button {
+ padding: 0.375rem 0.75rem;
+ border-radius: 0.375rem;
+ font-size: 0.8125rem;
+ cursor: pointer;
+ transition: background 0.15s;
+ }
+
+ button.update {
+ background: #fff;
+ colour: #6366f1;
+ border: 1px solid #6366f1;
+ }
+
+ button.update:hover {
+ background: #eef2ff;
+ }
+
+ button.update:disabled {
+ opacity: 0.5;
+ cursor: not-allowed;
+ }
+
+ button.remove {
+ background: #fff;
+ colour: #dc2626;
+ border: 1px solid #dc2626;
+ }
+
+ button.remove:hover {
+ background: #fef2f2;
+ }
+
+ .empty {
+ text-align: center;
+ padding: 2rem;
+ colour: #9ca3af;
+ font-size: 0.875rem;
+ }
+
+ .loading {
+ text-align: center;
+ padding: 2rem;
+ colour: #6b7280;
+ }
+
+ .error {
+ colour: #dc2626;
+ padding: 0.75rem;
+ background: #fef2f2;
+ border-radius: 0.375rem;
+ font-size: 0.875rem;
+ margin-bottom: 1rem;
+ }
+ `;
+M([
+ $({ attribute: "api-url" })
+], x.prototype, "apiUrl", 2);
+M([
+ p()
+], x.prototype, "modules", 2);
+M([
+ p()
+], x.prototype, "loading", 2);
+M([
+ p()
+], x.prototype, "error", 2);
+M([
+ p()
+], x.prototype, "updating", 2);
+x = M([
+ B("core-scm-installed")
+], x);
+var Ze = Object.defineProperty, Ge = Object.getOwnPropertyDescriptor, E = (t, e, s, r) => {
+ for (var i = r > 1 ? void 0 : r ? Ge(e, s) : e, o = t.length - 1, a; o >= 0; o--)
+ (a = t[o]) && (i = (r ? a(e, s, i) : a(i)) || i);
+ return r && i && Ze(e, s, i), i;
+};
+let g = class extends y {
+ constructor() {
+ super(...arguments), this.apiUrl = "", this.path = "", this.manifest = null, this.loading = !0, this.error = "", this.verifyKey = "", this.verifyResult = null;
+ }
+ connectedCallback() {
+ super.connectedCallback(), this.api = new X(this.apiUrl), this.loadManifest();
+ }
+ async loadManifest() {
+ this.loading = !0, this.error = "";
+ try {
+ this.manifest = await this.api.manifest();
+ } catch (t) {
+ this.error = t.message ?? "Failed to load manifest";
+ } finally {
+ this.loading = !1;
+ }
+ }
+ async refresh() {
+ this.verifyResult = null, await this.loadManifest();
+ }
+ async handleVerify() {
+ if (this.verifyKey.trim())
+ try {
+ this.verifyResult = await this.api.verify(this.verifyKey.trim());
+ } catch (t) {
+ this.error = t.message ?? "Verification failed";
+ }
+ }
+ async handleSign() {
+ const t = prompt("Enter hex-encoded Ed25519 private key:");
+ if (t)
+ try {
+ await this.api.sign(t), await this.loadManifest();
+ } catch (e) {
+ this.error = e.message ?? "Signing failed";
+ }
+ }
+ renderPermissions(t) {
+ if (!t) return c;
+ const e = [
+ { label: "Read", items: t.read },
+ { label: "Write", items: t.write },
+ { label: "Network", items: t.net },
+ { label: "Run", items: t.run }
+ ].filter((s) => s.items && s.items.length > 0);
+ return e.length === 0 ? c : l`
+
+
Permissions
+
+ ${e.map(
+ (s) => l`
+
+
${s.label}
+ ${s.items.map((r) => l`
${r}
`)}
+
+ `
+ )}
+
+
+ `;
+ }
+ render() {
+ if (this.loading)
+ return l`Loading manifest\u2026
`;
+ if (this.error && !this.manifest)
+ return l`${this.error}
`;
+ if (!this.manifest)
+ return l`No manifest found. Create a .core/manifest.yaml to get started.
`;
+ const t = this.manifest, e = !!t.sign;
+ return l`
+ ${this.error ? l`${this.error}
` : c}
+
+
+
+ ${t.description ? l`
+
+
Description
+
${t.description}
+
+ ` : c}
+ ${t.layout ? l`
+
+ ` : c}
+ ${t.slots && Object.keys(t.slots).length > 0 ? l`
+
+
Slots
+
+ ${Object.entries(t.slots).map(
+ ([s, r]) => l`
+ ${s}
+ ${r}
+ `
+ )}
+
+
+ ` : c}
+
+ ${this.renderPermissions(t.permissions)}
+ ${t.modules && t.modules.length > 0 ? l`
+
+
Modules
+ ${t.modules.map((s) => l`
${s}
`)}
+
+ ` : c}
+
+
+
+ ${e ? this.verifyResult ? this.verifyResult.valid ? "Verified" : "Invalid" : "Signed" : "Unsigned"}
+
+ ${e ? l`Signature present` : l`No signature`}
+
+
+
+ this.verifyKey = s.target.value}
+ />
+
+
+
+
+ `;
+ }
+};
+g.styles = W`
+ :host {
+ display: block;
+ font-family: system-ui, -apple-system, sans-serif;
+ }
+
+ .manifest {
+ border: 1px solid #e5e7eb;
+ border-radius: 0.5rem;
+ padding: 1.25rem;
+ background: #fff;
+ }
+
+ .header {
+ display: flex;
+ justify-content: space-between;
+ align-items: flex-start;
+ margin-bottom: 1rem;
+ }
+
+ h3 {
+ margin: 0;
+ font-size: 1.125rem;
+ font-weight: 600;
+ }
+
+ .version {
+ font-size: 0.75rem;
+ padding: 0.125rem 0.5rem;
+ background: #f3f4f6;
+ border-radius: 1rem;
+ colour: #6b7280;
+ }
+
+ .field {
+ margin-bottom: 0.75rem;
+ }
+
+ .field-label {
+ font-size: 0.75rem;
+ font-weight: 600;
+ colour: #6b7280;
+ text-transform: uppercase;
+ letter-spacing: 0.025em;
+ margin-bottom: 0.25rem;
+ }
+
+ .field-value {
+ font-size: 0.875rem;
+ }
+
+ .code {
+ font-family: monospace;
+ font-size: 0.8125rem;
+ background: #f9fafb;
+ padding: 0.25rem 0.5rem;
+ border-radius: 0.25rem;
+ }
+
+ .slots {
+ display: grid;
+ grid-template-columns: auto 1fr;
+ gap: 0.25rem 1rem;
+ font-size: 0.8125rem;
+ }
+
+ .slot-key {
+ font-weight: 600;
+ colour: #374151;
+ }
+
+ .slot-value {
+ font-family: monospace;
+ colour: #6b7280;
+ }
+
+ .permissions-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
+ gap: 0.5rem;
+ }
+
+ .perm-group {
+ border: 1px solid #e5e7eb;
+ border-radius: 0.375rem;
+ padding: 0.5rem;
+ }
+
+ .perm-group-label {
+ font-size: 0.6875rem;
+ font-weight: 700;
+ colour: #6b7280;
+ text-transform: uppercase;
+ margin-bottom: 0.25rem;
+ }
+
+ .perm-item {
+ font-size: 0.8125rem;
+ font-family: monospace;
+ colour: #374151;
+ }
+
+ .signature {
+ display: flex;
+ align-items: center;
+ gap: 0.75rem;
+ margin-top: 1rem;
+ padding: 0.75rem;
+ border-radius: 0.375rem;
+ font-size: 0.875rem;
+ }
+
+ .signature.signed {
+ background: #f0fdf4;
+ border: 1px solid #bbf7d0;
+ }
+
+ .signature.unsigned {
+ background: #fffbeb;
+ border: 1px solid #fde68a;
+ }
+
+ .signature.invalid {
+ background: #fef2f2;
+ border: 1px solid #fecaca;
+ }
+
+ .badge {
+ font-size: 0.75rem;
+ font-weight: 600;
+ padding: 0.125rem 0.5rem;
+ border-radius: 1rem;
+ }
+
+ .badge.verified {
+ background: #dcfce7;
+ colour: #166534;
+ }
+
+ .badge.unsigned {
+ background: #fef3c7;
+ colour: #92400e;
+ }
+
+ .badge.invalid {
+ background: #fee2e2;
+ colour: #991b1b;
+ }
+
+ .actions {
+ margin-top: 1rem;
+ display: flex;
+ gap: 0.5rem;
+ }
+
+ .verify-input {
+ flex: 1;
+ padding: 0.375rem 0.75rem;
+ border: 1px solid #d1d5db;
+ border-radius: 0.375rem;
+ font-size: 0.8125rem;
+ font-family: monospace;
+ }
+
+ button {
+ padding: 0.375rem 1rem;
+ border-radius: 0.375rem;
+ font-size: 0.8125rem;
+ cursor: pointer;
+ border: 1px solid #d1d5db;
+ background: #fff;
+ transition: background 0.15s;
+ }
+
+ button:hover {
+ background: #f3f4f6;
+ }
+
+ button.primary {
+ background: #6366f1;
+ colour: #fff;
+ border-colour: #6366f1;
+ }
+
+ button.primary:hover {
+ background: #4f46e5;
+ }
+
+ .empty {
+ text-align: center;
+ padding: 2rem;
+ colour: #9ca3af;
+ font-size: 0.875rem;
+ }
+
+ .error {
+ colour: #dc2626;
+ padding: 0.75rem;
+ background: #fef2f2;
+ border-radius: 0.375rem;
+ font-size: 0.875rem;
+ }
+
+ .loading {
+ text-align: center;
+ padding: 2rem;
+ colour: #6b7280;
+ }
+ `;
+E([
+ $({ attribute: "api-url" })
+], g.prototype, "apiUrl", 2);
+E([
+ $()
+], g.prototype, "path", 2);
+E([
+ p()
+], g.prototype, "manifest", 2);
+E([
+ p()
+], g.prototype, "loading", 2);
+E([
+ p()
+], g.prototype, "error", 2);
+E([
+ p()
+], g.prototype, "verifyKey", 2);
+E([
+ p()
+], g.prototype, "verifyResult", 2);
+g = E([
+ B("core-scm-manifest")
+], g);
+var Xe = Object.defineProperty, Ye = Object.getOwnPropertyDescriptor, F = (t, e, s, r) => {
+ for (var i = r > 1 ? void 0 : r ? Ye(e, s) : e, o = t.length - 1, a; o >= 0; o--)
+ (a = t[o]) && (i = (r ? a(e, s, i) : a(i)) || i);
+ return r && i && Xe(e, s, i), i;
+};
+let O = class extends y {
+ constructor() {
+ super(...arguments), this.apiUrl = "", this.repos = [], this.loading = !0, this.error = "";
+ }
+ connectedCallback() {
+ super.connectedCallback(), this.api = new X(this.apiUrl), this.loadRegistry();
+ }
+ async loadRegistry() {
+ this.loading = !0, this.error = "";
+ try {
+ this.repos = await this.api.registry();
+ } catch (t) {
+ this.error = t.message ?? "Failed to load registry";
+ } finally {
+ this.loading = !1;
+ }
+ }
+ async refresh() {
+ await this.loadRegistry();
+ }
+ render() {
+ return this.loading ? l`Loading registry\u2026
` : l`
+ ${this.error ? l`${this.error}
` : c}
+ ${this.repos.length === 0 ? l`No repositories in registry.
` : l`
+
+ ${this.repos.map(
+ (t) => l`
+
+
+
${t.name}
+ ${t.description ? l`
${t.description}
` : c}
+
+ ${t.type}
+ ${t.depends_on && t.depends_on.length > 0 ? l`depends: ${t.depends_on.join(", ")}` : c}
+
+
+
+
+ ${t.exists ? "Present" : "Missing"}
+
+
+ `
+ )}
+
+ `}
+ `;
+ }
+};
+O.styles = W`
+ :host {
+ display: block;
+ font-family: system-ui, -apple-system, sans-serif;
+ }
+
+ .list {
+ display: flex;
+ flex-direction: column;
+ gap: 0.375rem;
+ }
+
+ .repo {
+ border: 1px solid #e5e7eb;
+ border-radius: 0.5rem;
+ padding: 0.75rem 1rem;
+ background: #fff;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ }
+
+ .repo-info {
+ flex: 1;
+ }
+
+ .repo-name {
+ font-weight: 600;
+ font-size: 0.9375rem;
+ font-family: monospace;
+ }
+
+ .repo-desc {
+ font-size: 0.8125rem;
+ colour: #6b7280;
+ margin-top: 0.125rem;
+ }
+
+ .repo-meta {
+ display: flex;
+ gap: 0.5rem;
+ align-items: center;
+ margin-top: 0.25rem;
+ }
+
+ .type-badge {
+ font-size: 0.6875rem;
+ padding: 0.0625rem 0.5rem;
+ border-radius: 1rem;
+ font-weight: 600;
+ }
+
+ .type-badge.foundation {
+ background: #dbeafe;
+ colour: #1e40af;
+ }
+
+ .type-badge.module {
+ background: #f3e8ff;
+ colour: #6b21a8;
+ }
+
+ .type-badge.product {
+ background: #dcfce7;
+ colour: #166534;
+ }
+
+ .type-badge.template {
+ background: #fef3c7;
+ colour: #92400e;
+ }
+
+ .deps {
+ font-size: 0.75rem;
+ colour: #9ca3af;
+ }
+
+ .status {
+ display: flex;
+ align-items: center;
+ gap: 0.375rem;
+ }
+
+ .status-dot {
+ width: 0.5rem;
+ height: 0.5rem;
+ border-radius: 50%;
+ }
+
+ .status-dot.present {
+ background: #22c55e;
+ }
+
+ .status-dot.missing {
+ background: #ef4444;
+ }
+
+ .status-label {
+ font-size: 0.75rem;
+ colour: #6b7280;
+ }
+
+ .empty {
+ text-align: center;
+ padding: 2rem;
+ colour: #9ca3af;
+ font-size: 0.875rem;
+ }
+
+ .loading {
+ text-align: center;
+ padding: 2rem;
+ colour: #6b7280;
+ }
+
+ .error {
+ colour: #dc2626;
+ padding: 0.75rem;
+ background: #fef2f2;
+ border-radius: 0.375rem;
+ font-size: 0.875rem;
+ margin-bottom: 1rem;
+ }
+ `;
+F([
+ $({ attribute: "api-url" })
+], O.prototype, "apiUrl", 2);
+F([
+ p()
+], O.prototype, "repos", 2);
+F([
+ p()
+], O.prototype, "loading", 2);
+F([
+ p()
+], O.prototype, "error", 2);
+O = F([
+ B("core-scm-registry")
+], O);
+var et = Object.defineProperty, tt = Object.getOwnPropertyDescriptor, N = (t, e, s, r) => {
+ for (var i = r > 1 ? void 0 : r ? tt(e, s) : e, o = t.length - 1, a; o >= 0; o--)
+ (a = t[o]) && (i = (r ? a(e, s, i) : a(i)) || i);
+ return r && i && et(e, s, i), i;
+};
+let S = class extends y {
+ constructor() {
+ super(...arguments), this.apiUrl = "", this.wsUrl = "", this.activeTab = "marketplace", this.wsConnected = !1, this.lastEvent = "", this.ws = null, this.tabs = [
+ { id: "marketplace", label: "Marketplace" },
+ { id: "installed", label: "Installed" },
+ { id: "manifest", label: "Manifest" },
+ { id: "registry", label: "Registry" }
+ ];
+ }
+ connectedCallback() {
+ super.connectedCallback(), this.wsUrl && this.connectWs();
+ }
+ updated(t) {
+ super.updated(t), t.has("wsUrl") && this.isConnected && this.connectWs();
+ }
+ disconnectedCallback() {
+ super.disconnectedCallback(), this.disconnectWs();
+ }
+ connectWs() {
+ this.disconnectWs(), this.wsUrl && (this.ws = Be(this.wsUrl, (t) => {
+ this.lastEvent = t.channel ?? t.type ?? "", this.requestUpdate(), this.refreshForEvent(t);
+ }), this.ws.onopen = () => {
+ this.wsConnected = !0;
+ }, this.ws.onclose = () => {
+ this.wsConnected = !1;
+ });
+ }
+ disconnectWs() {
+ this.ws && (this.ws.close(), this.ws = null);
+ }
+ handleTabClick(t) {
+ this.activeTab = t;
+ }
+ async handleRefresh() {
+ await this.refreshActiveTab();
+ }
+ refreshForEvent(t) {
+ this.tabsForChannel(t.channel ?? t.type ?? "").includes(this.activeTab) && this.refreshActiveTab();
+ }
+ tabsForChannel(t) {
+ return t.startsWith("scm.marketplace.") ? ["marketplace", "installed"] : t.startsWith("scm.installed.") ? ["installed"] : t === "scm.manifest.verified" ? ["manifest"] : t === "scm.registry.changed" ? ["registry"] : [];
+ }
+ async refreshActiveTab() {
+ var e;
+ const t = (e = this.shadowRoot) == null ? void 0 : e.querySelector(".content > *");
+ t != null && t.refresh && await t.refresh();
+ }
+ renderContent() {
+ switch (this.activeTab) {
+ case "marketplace":
+ return l``;
+ case "installed":
+ return l``;
+ case "manifest":
+ return l``;
+ case "registry":
+ return l``;
+ default:
+ return c;
+ }
+ }
+ render() {
+ const t = this.wsUrl ? this.wsConnected ? "connected" : "disconnected" : "idle";
+ return l`
+
+
+
+ ${this.tabs.map(
+ (e) => l`
+
+ `
+ )}
+
+
+ ${this.renderContent()}
+
+
+ `;
+ }
+};
+S.styles = W`
+ :host {
+ display: flex;
+ flex-direction: column;
+ font-family: system-ui, -apple-system, sans-serif;
+ height: 100%;
+ background: #fafafa;
+ }
+
+ /* H — Header */
+ .header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 0.75rem 1rem;
+ background: #fff;
+ border-bottom: 1px solid #e5e7eb;
+ }
+
+ .title {
+ font-weight: 700;
+ font-size: 1rem;
+ colour: #111827;
+ }
+
+ .refresh-btn {
+ padding: 0.375rem 0.75rem;
+ border: 1px solid #d1d5db;
+ border-radius: 0.375rem;
+ background: #fff;
+ font-size: 0.8125rem;
+ cursor: pointer;
+ transition: background 0.15s;
+ }
+
+ .refresh-btn:hover {
+ background: #f3f4f6;
+ }
+
+ /* H-L — Tabs */
+ .tabs {
+ display: flex;
+ gap: 0;
+ background: #fff;
+ border-bottom: 1px solid #e5e7eb;
+ padding: 0 1rem;
+ }
+
+ .tab {
+ padding: 0.625rem 1rem;
+ font-size: 0.8125rem;
+ font-weight: 500;
+ colour: #6b7280;
+ cursor: pointer;
+ border-bottom: 2px solid transparent;
+ transition: all 0.15s;
+ background: none;
+ border-top: none;
+ border-left: none;
+ border-right: none;
+ }
+
+ .tab:hover {
+ colour: #374151;
+ }
+
+ .tab.active {
+ colour: #6366f1;
+ border-bottom-colour: #6366f1;
+ }
+
+ /* C — Content */
+ .content {
+ flex: 1;
+ padding: 1rem;
+ overflow-y: auto;
+ }
+
+ /* F — Footer / Status bar */
+ .footer {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 0.5rem 1rem;
+ background: #fff;
+ border-top: 1px solid #e5e7eb;
+ font-size: 0.75rem;
+ colour: #9ca3af;
+ }
+
+ .ws-status {
+ display: flex;
+ align-items: center;
+ gap: 0.375rem;
+ }
+
+ .ws-dot {
+ width: 0.5rem;
+ height: 0.5rem;
+ border-radius: 50%;
+ }
+
+ .ws-dot.connected {
+ background: #22c55e;
+ }
+
+ .ws-dot.disconnected {
+ background: #ef4444;
+ }
+
+ .ws-dot.idle {
+ background: #d1d5db;
+ }
+ `;
+N([
+ $({ attribute: "api-url" })
+], S.prototype, "apiUrl", 2);
+N([
+ $({ attribute: "ws-url" })
+], S.prototype, "wsUrl", 2);
+N([
+ p()
+], S.prototype, "activeTab", 2);
+N([
+ p()
+], S.prototype, "wsConnected", 2);
+N([
+ p()
+], S.prototype, "lastEvent", 2);
+S = N([
+ B("core-scm-panel")
+], S);
+export {
+ X as ScmApi,
+ x as ScmInstalled,
+ g as ScmManifest,
+ m as ScmMarketplace,
+ S as ScmPanel,
+ O as ScmRegistry,
+ Be as connectScmEvents
+};
diff --git a/ui/src/scm-installed.ts b/ui/src/scm-installed.ts
index 173cf88..56cea05 100644
--- a/ui/src/scm-installed.ts
+++ b/ui/src/scm-installed.ts
@@ -162,6 +162,10 @@ export class ScmInstalled extends LitElement {
}
}
+ async refresh() {
+ await this.loadInstalled();
+ }
+
private async handleUpdate(code: string) {
this.updating = new Set([...this.updating, code]);
try {
diff --git a/ui/src/scm-manifest.ts b/ui/src/scm-manifest.ts
index cb8cf77..1cbe553 100644
--- a/ui/src/scm-manifest.ts
+++ b/ui/src/scm-manifest.ts
@@ -265,6 +265,11 @@ export class ScmManifest extends LitElement {
}
}
+ async refresh() {
+ this.verifyResult = null;
+ await this.loadManifest();
+ }
+
private async handleVerify() {
if (!this.verifyKey.trim()) return;
try {
diff --git a/ui/src/scm-marketplace.ts b/ui/src/scm-marketplace.ts
index ad3b94a..cee41dc 100644
--- a/ui/src/scm-marketplace.ts
+++ b/ui/src/scm-marketplace.ts
@@ -219,6 +219,10 @@ export class ScmMarketplace extends LitElement {
}
}
+ async refresh() {
+ await this.loadModules();
+ }
+
private handleSearch(e: Event) {
this.searchQuery = (e.target as HTMLInputElement).value;
this.loadModules();
diff --git a/ui/src/scm-panel.ts b/ui/src/scm-panel.ts
index dd55df3..3b5978d 100644
--- a/ui/src/scm-panel.ts
+++ b/ui/src/scm-panel.ts
@@ -2,6 +2,7 @@
import { LitElement, html, css, nothing } from 'lit';
import { customElement, property, state } from 'lit/decorators.js';
+import type { PropertyValues } from 'lit';
import { connectScmEvents, type ScmEvent } from './shared/events.js';
// Side-effect imports to register child elements
@@ -11,6 +12,9 @@ import './scm-manifest.js';
import './scm-registry.js';
type TabId = 'marketplace' | 'installed' | 'manifest' | 'registry';
+type RefreshableElement = HTMLElement & {
+ refresh?: () => Promise | void;
+};
/**
* — Top-level HLCRF panel with tabs.
@@ -154,18 +158,28 @@ export class ScmPanel extends LitElement {
}
}
- disconnectedCallback() {
- super.disconnectedCallback();
- if (this.ws) {
- this.ws.close();
- this.ws = null;
+ updated(changedProperties: PropertyValues) {
+ super.updated(changedProperties);
+ if (changedProperties.has('wsUrl') && this.isConnected) {
+ this.connectWs();
}
}
+ disconnectedCallback() {
+ super.disconnectedCallback();
+ this.disconnectWs();
+ }
+
private connectWs() {
+ this.disconnectWs();
+ if (!this.wsUrl) {
+ return;
+ }
+
this.ws = connectScmEvents(this.wsUrl, (event: ScmEvent) => {
this.lastEvent = event.channel ?? event.type ?? '';
this.requestUpdate();
+ this.refreshForEvent(event);
});
this.ws.onopen = () => {
this.wsConnected = true;
@@ -175,27 +189,55 @@ export class ScmPanel extends LitElement {
};
}
+ private disconnectWs() {
+ if (!this.ws) {
+ return;
+ }
+
+ this.ws.close();
+ this.ws = null;
+ }
+
private handleTabClick(tab: TabId) {
this.activeTab = tab;
}
- private handleRefresh() {
- // Force re-render of active child by toggling a key
- const content = this.shadowRoot?.querySelector('.content');
- if (content) {
- const child = content.firstElementChild;
- if (child && 'loadModules' in child) {
- (child as any).loadModules();
- } else if (child && 'loadInstalled' in child) {
- (child as any).loadInstalled();
- } else if (child && 'loadManifest' in child) {
- (child as any).loadManifest();
- } else if (child && 'loadRegistry' in child) {
- (child as any).loadRegistry();
- }
+ private async handleRefresh() {
+ await this.refreshActiveTab();
+ }
+
+ private refreshForEvent(event: ScmEvent) {
+ const targets = this.tabsForChannel(event.channel ?? event.type ?? '');
+ if (targets.includes(this.activeTab)) {
+ void this.refreshActiveTab();
}
}
+ private tabsForChannel(channel: string): TabId[] {
+ if (channel.startsWith('scm.marketplace.')) {
+ return ['marketplace', 'installed'];
+ }
+ if (channel.startsWith('scm.installed.')) {
+ return ['installed'];
+ }
+ if (channel === 'scm.manifest.verified') {
+ return ['manifest'];
+ }
+ if (channel === 'scm.registry.changed') {
+ return ['registry'];
+ }
+ return [];
+ }
+
+ private async refreshActiveTab() {
+ const child = this.shadowRoot?.querySelector('.content > *') as RefreshableElement | null;
+ if (!child?.refresh) {
+ return;
+ }
+
+ await child.refresh();
+ }
+
private renderContent() {
switch (this.activeTab) {
case 'marketplace':
diff --git a/ui/src/scm-registry.ts b/ui/src/scm-registry.ts
index df26165..5305ff3 100644
--- a/ui/src/scm-registry.ts
+++ b/ui/src/scm-registry.ts
@@ -170,6 +170,10 @@ export class ScmRegistry extends LitElement {
}
}
+ async refresh() {
+ await this.loadRegistry();
+ }
+
render() {
if (this.loading) {
return html`Loading registry\u2026
`;
diff --git a/ui/src/shared/api.ts b/ui/src/shared/api.ts
index 1b272cf..9860e02 100644
--- a/ui/src/shared/api.ts
+++ b/ui/src/shared/api.ts
@@ -12,9 +12,12 @@ export class ScmApi {
private async request(path: string, opts?: RequestInit): Promise {
const res = await fetch(`${this.base}${path}`, opts);
- const json = await res.json();
- if (!json.success) {
- throw new Error(json.error?.message ?? 'Request failed');
+ const json = await res.json().catch(() => null);
+ if (!res.ok) {
+ throw new Error(json?.error?.message ?? `Request failed (${res.status})`);
+ }
+ if (!json?.success) {
+ throw new Error(json?.error?.message ?? 'Request failed');
}
return json.data as T;
}
@@ -28,15 +31,15 @@ export class ScmApi {
}
marketplaceItem(code: string) {
- return this.request(`/marketplace/${code}`);
+ return this.request(`/marketplace/${encodeURIComponent(code)}`);
}
install(code: string) {
- return this.request(`/marketplace/${code}/install`, { method: 'POST' });
+ return this.request(`/marketplace/${encodeURIComponent(code)}/install`, { method: 'POST' });
}
remove(code: string) {
- return this.request(`/marketplace/${code}`, { method: 'DELETE' });
+ return this.request(`/marketplace/${encodeURIComponent(code)}`, { method: 'DELETE' });
}
installed() {
@@ -44,7 +47,7 @@ export class ScmApi {
}
updateInstalled(code: string) {
- return this.request(`/installed/${code}/update`, { method: 'POST' });
+ return this.request(`/installed/${encodeURIComponent(code)}/update`, { method: 'POST' });
}
manifest() {