diff --git a/custom_components/bahmcloud_store/panel/panel.js b/custom_components/bahmcloud_store/panel/panel.js index 092e27b..c7743a0 100644 --- a/custom_components/bahmcloud_store/panel/panel.js +++ b/custom_components/bahmcloud_store/panel/panel.js @@ -10,9 +10,6 @@ class BahmcloudStorePanel extends HTMLElement { this._loading = true; this._error = null; - this._customAddUrl = ""; - this._customAddName = ""; - this._search = ""; this._category = "all"; @@ -60,10 +57,7 @@ class BahmcloudStorePanel extends HTMLElement { if (this._isDesktop()) return; try { this.dispatchEvent(new Event("hass-toggle-menu", { bubbles: true, composed: true })); - return; } catch (_) {} - this._error = "Unable to open the sidebar on this client. Use the back button."; - this._update(); } _goBack() { @@ -108,7 +102,10 @@ class BahmcloudStorePanel extends HTMLElement { this._update(); try { - const resp = await this._hass.callApi("get", `bcs/readme?repo_id=${encodeURIComponent(repoId)}`); + const resp = await this._hass.callApi( + "get", + `bcs/readme?repo_id=${encodeURIComponent(repoId)}` + ); if (resp?.ok && typeof resp.readme === "string" && resp.readme.trim()) { this._readmeText = resp.readme; @@ -164,38 +161,13 @@ class BahmcloudStorePanel extends HTMLElement { color:var(--primary-text-color); } - .tabs{ display:flex; gap:8px; flex-wrap:wrap; margin-bottom:12px; } - .tab{ - border:1px solid var(--divider-color); - background:var(--card-background-color); - color:var(--primary-text-color); - padding:8px 12px; border-radius:999px; - cursor:pointer; font-weight:700; font-size:13px; - } - .tab.active{ - border-color:var(--bcs-accent); - box-shadow:0 0 0 2px color-mix(in srgb, var(--bcs-accent) 20%, transparent); - } - - button{ - padding:9px 12px; border-radius:12px; - border:1px solid var(--divider-color); - background:var(--card-background-color); - color:var(--primary-text-color); - cursor:pointer; font-weight:800; - } - button.primary{ - border-color:var(--bcs-accent); - background: color-mix(in srgb, var(--bcs-accent) 16%, var(--card-background-color)); - } - .card{ border:1px solid var(--divider-color); background:var(--card-background-color); border-radius:16px; padding:12px; margin:10px 0; } - .card.clickable{ cursor:pointer; } + .row{ display:flex; justify-content:space-between; gap:10px; align-items:flex-start; } .muted{ color:var(--secondary-text-color); font-size:13px; margin-top:4px; } .small{ font-size:12px; } @@ -207,18 +179,7 @@ class BahmcloudStorePanel extends HTMLElement { } .badge.custom{ border-color:var(--bcs-accent); color:var(--bcs-accent); } - .error{ color:#b00020; white-space:pre-wrap; margin-top:10px; } - - .grid2{ display:grid; grid-template-columns: 1fr; gap: 12px; } - @media (min-width: 900px){ .grid2{ grid-template-columns: 1.2fr 0.8fr; } } - - .filters{ - display:flex; - gap: 10px; - flex-wrap: wrap; - align-items: center; - margin-bottom: 12px; - } + .filters{ display:flex; gap:10px; flex-wrap:wrap; align-items:center; margin-bottom:12px; } input, select{ padding:10px 12px; border-radius:12px; @@ -235,23 +196,8 @@ class BahmcloudStorePanel extends HTMLElement { a{ color:var(--bcs-accent); text-decoration:none; } a:hover{ text-decoration:underline; } - pre.readme{ - white-space: pre-wrap; - word-break: break-word; - margin: 0; - font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; - font-size: 12.5px; - line-height: 1.5; - } - - details{ margin-top: 10px; } - summary{ cursor:pointer; color: var(--bcs-accent); font-weight: 800; } - - /* Pretty markdown styling */ - .md{ - line-height: 1.6; - font-size: 14px; - } + /* Pretty markdown styling for server-rendered HTML */ + .md { line-height: 1.65; font-size: 14px; } .md h1,.md h2,.md h3{ margin: 18px 0 10px; } .md p{ margin: 10px 0; } .md code{ padding: 2px 6px; border-radius: 8px; border: 1px solid var(--divider-color); } @@ -268,15 +214,18 @@ class BahmcloudStorePanel extends HTMLElement { background: color-mix(in srgb, var(--bcs-accent) 8%, var(--card-background-color)); border-radius: 12px; } - .md table{ - width:100%; - border-collapse: collapse; - } - .md th,.md td{ - border: 1px solid var(--divider-color); - padding: 8px; - text-align: left; + + pre.readme{ + white-space: pre-wrap; + word-break: break-word; + margin: 0; + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + font-size: 12.5px; + line-height: 1.5; } + + details{ margin-top: 10px; } + summary{ cursor:pointer; color: var(--bcs-accent); font-weight: 800; }
@@ -294,14 +243,8 @@ class BahmcloudStorePanel extends HTMLElement {
-
-
Store
-
Manage repositories
-
Settings / About
-
-
-
+
`; @@ -309,13 +252,6 @@ class BahmcloudStorePanel extends HTMLElement { root.getElementById("menuBtn").addEventListener("click", () => this._toggleMenu()); root.getElementById("backBtn").addEventListener("click", () => this._goBack()); - for (const tab of root.querySelectorAll(".tab")) { - tab.addEventListener("click", () => { - this._view = tab.getAttribute("data-view"); - this._update(); - }); - } - // Prevent HA global shortcuts while typing inside panel inputs const stopIfFormField = (e) => { const t = e.composedPath ? e.composedPath()[0] : e.target; @@ -330,87 +266,34 @@ class BahmcloudStorePanel extends HTMLElement { root.addEventListener("keypress", stopIfFormField, true); } - _captureFocusState() { - const root = this.shadowRoot; - const ae = root.activeElement; - if (!ae || !ae.id) return null; - - const supported = new Set(["searchInput", "categorySelect"]); - if (!supported.has(ae.id)) return null; - - return { - id: ae.id, - value: ae.value, - selectionStart: typeof ae.selectionStart === "number" ? ae.selectionStart : null, - selectionEnd: typeof ae.selectionEnd === "number" ? ae.selectionEnd : null, - }; - } - - _restoreFocusState(state) { - if (!state) return; - const root = this.shadowRoot; - const el = root.getElementById(state.id); - if (!el) return; - - try { - el.focus({ preventScroll: true }); - if (state.selectionStart !== null && state.selectionEnd !== null && typeof el.setSelectionRange === "function") { - el.setSelectionRange(state.selectionStart, state.selectionEnd); - } - } catch (_) {} - } - _update() { const root = this.shadowRoot; - const focusState = this._captureFocusState(); - const content = root.getElementById("content"); const err = root.getElementById("error"); const subtitle = root.getElementById("subtitle"); const v = this._data?.version ? String(this._data.version) : null; subtitle.textContent = v ? `BCS ${v}` : "BCS — loading…"; - - for (const tab of root.querySelectorAll(".tab")) { - tab.classList.toggle("active", tab.getAttribute("data-view") === this._view); - } - err.textContent = this._error ? `Error: ${this._error}` : ""; if (this._loading) { content.innerHTML = `
Loading…
`; - this._restoreFocusState(focusState); return; } if (!this._data) { content.innerHTML = `
No data.
`; - this._restoreFocusState(focusState); return; } - if (this._view === "store") { - content.innerHTML = this._renderStore(); - this._wireStore(); - this._restoreFocusState(focusState); + if (this._view === "detail") { + content.innerHTML = this._renderDetail(); + this._wireDetail(); return; } - if (this._view === "manage") { - content.innerHTML = `
Manage view is unchanged in this patch.
`; - this._restoreFocusState(focusState); - return; - } - - if (this._view === "about") { - content.innerHTML = this._renderAbout(); - this._restoreFocusState(focusState); - return; - } - - content.innerHTML = this._renderDetail(); - this._wireDetail(); - this._restoreFocusState(focusState); + content.innerHTML = this._renderStore(); + this._wireStore(); } _renderStore() { @@ -442,9 +325,6 @@ class BahmcloudStorePanel extends HTMLElement { const desc = r.description || "No description available."; const creator = r.owner ? `Creator: ${r.owner}` : "Creator: -"; const latest = r.latest_version ? `Latest: ${r.latest_version}` : "Latest: unknown"; - const metaSrc = r.meta_source ? `Meta: ${r.meta_source}` : null; - - const lineBits = [creator, latest, metaSrc].filter(Boolean); return `
@@ -452,7 +332,7 @@ class BahmcloudStorePanel extends HTMLElement {
${this._esc(r.name)}
${this._esc(desc)}
-
${this._esc(lineBits.join(" · "))}
+
${this._esc(`${creator} · ${latest}`)}
${badge}
@@ -480,7 +360,6 @@ class BahmcloudStorePanel extends HTMLElement { this._update(); }); } - if (cat) { cat.addEventListener("change", (e) => { this._category = String(e.target.value || "all"); @@ -500,19 +379,7 @@ class BahmcloudStorePanel extends HTMLElement { const r = this._detailRepo; if (!r) return `
Repository not found.
`; - const badge = r.source === "custom" - ? `Custom` - : `Index`; - const desc = r.description || "No description available."; - const latest = r.latest_version ? `Latest: ${r.latest_version}` : "Latest: unknown"; - - const infoBits = [ - r.owner ? `Creator: ${r.owner}` : "Creator: -", - latest, - r.provider ? `Provider: ${r.provider}` : null, - r.meta_source ? `Meta: ${r.meta_source}` : null, - ].filter(Boolean); const readmeBlock = this._readmeLoading ? `
Loading README…
` @@ -538,32 +405,19 @@ class BahmcloudStorePanel extends HTMLElement { `; return ` -
-
-
-
-
-
${this._esc(r.name)}
-
${this._esc(desc)}
-
${this._esc(infoBits.join(" · "))}
- -
- ${badge} +
+
+
+
${this._esc(r.name)}
+
${this._esc(desc)}
+
- - ${readmeBlock} -
- -
-
-
Installation & Updates
-
Buttons will be enabled in a later version.
-
+ ${this._esc(r.source === "custom" ? "Custom" : "Index")}
+ ${readmeBlock} `; } @@ -573,22 +427,14 @@ class BahmcloudStorePanel extends HTMLElement { if (!pretty) return; if (this._readmeHtml) { + // ✅ pretty HTML from backend pretty.innerHTML = this._readmeHtml; } else if (this._readmeText) { - // If backend could not render HTML, show a friendly fallback text - pretty.innerHTML = `
Markdown rendering is not available. Please use "Show raw Markdown".
`; + // fallback: no renderer available in backend + pretty.innerHTML = `
Markdown rendering is not available on this system. Use “Show raw Markdown”.
`; } } - _renderAbout() { - return ` -
-
Settings / About
-
BCS version: ${this._esc(this._data.version || "-")}
-
- `; - } - _esc(s) { return String(s ?? "") .replaceAll("&", "&")