diff --git a/custom_components/bahmcloud_store/panel/panel.js b/custom_components/bahmcloud_store/panel/panel.js
index ef0a588..98b500e 100644
--- a/custom_components/bahmcloud_store/panel/panel.js
+++ b/custom_components/bahmcloud_store/panel/panel.js
@@ -5,9 +5,7 @@ class BahmcloudStorePanel extends HTMLElement {
this._hass = null;
- // Views: store | manage | about | detail
this._view = "store";
-
this._data = null;
this._loading = true;
this._error = null;
@@ -15,11 +13,9 @@ class BahmcloudStorePanel extends HTMLElement {
this._customAddUrl = "";
this._customAddName = "";
- // Store filtering
this._search = "";
this._category = "all";
- // Detail view state
this._detailRepoId = null;
this._detailRepo = null;
this._readmeLoading = false;
@@ -81,7 +77,6 @@ class BahmcloudStorePanel extends HTMLElement {
}
_goBack() {
- // If we're in detail, go back to store list first.
if (this._view === "detail") {
this._view = "store";
this._detailRepoId = null;
@@ -244,10 +239,7 @@ class BahmcloudStorePanel extends HTMLElement {
border-color:var(--bcs-accent);
background: color-mix(in srgb, var(--bcs-accent) 16%, var(--card-background-color));
}
- button:disabled{
- opacity: 0.55;
- cursor: not-allowed;
- }
+ button:disabled{ opacity: 0.55; cursor: not-allowed; }
.card{
border:1px solid var(--divider-color);
@@ -277,14 +269,8 @@ class BahmcloudStorePanel extends HTMLElement {
.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; }
- }
+ .grid2{ display:grid; grid-template-columns: 1fr; gap: 12px; }
+ @media (min-width: 900px){ .grid2{ grid-template-columns: 1.2fr 0.8fr; } }
.filters{
display:flex;
@@ -309,7 +295,6 @@ class BahmcloudStorePanel extends HTMLElement {
a{ color:var(--bcs-accent); text-decoration:none; }
a:hover{ text-decoration:underline; }
- /* FABs (detail view) */
.fabs{
position: fixed;
right: 18px;
@@ -337,12 +322,8 @@ class BahmcloudStorePanel extends HTMLElement {
border-color: var(--bcs-accent);
background: color-mix(in srgb, var(--bcs-accent) 18%, var(--card-background-color));
}
- .fab[disabled]{
- opacity: .55;
- cursor: not-allowed;
- }
+ .fab[disabled]{ opacity: .55; cursor: not-allowed; }
- /* README fallback pre */
pre.readme{
white-space: pre-wrap;
word-break: break-word;
@@ -351,6 +332,9 @@ class BahmcloudStorePanel extends HTMLElement {
font-size: 12.5px;
line-height: 1.5;
}
+
+ details{ margin-top: 10px; }
+ summary{ cursor:pointer; color: var(--bcs-accent); font-weight: 800; }
@@ -392,8 +376,7 @@ class BahmcloudStorePanel extends HTMLElement {
});
}
- // ✅ CRITICAL FIX: prevent HA global shortcuts (Assist/Command Palette) from
- // stealing keystrokes while typing in inputs inside this panel.
+ // Prevent HA global shortcuts while typing inside panel inputs
const stopIfFormField = (e) => {
const t = e.composedPath ? e.composedPath()[0] : e.target;
if (!t) return;
@@ -410,14 +393,47 @@ class BahmcloudStorePanel extends HTMLElement {
}
};
- // Use capture phase so we intercept before HA handlers.
root.addEventListener("keydown", stopIfFormField, true);
root.addEventListener("keyup", stopIfFormField, true);
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", "addUrl", "addName"]);
+ if (!supported.has(ae.id)) return null;
+
+ const state = {
+ id: ae.id,
+ value: ae.value,
+ selectionStart: typeof ae.selectionStart === "number" ? ae.selectionStart : null,
+ selectionEnd: typeof ae.selectionEnd === "number" ? ae.selectionEnd : null,
+ };
+ return state;
+ }
+
+ _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");
@@ -437,34 +453,39 @@ class BahmcloudStorePanel extends HTMLElement {
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);
return;
}
if (this._view === "manage") {
content.innerHTML = this._renderManage();
this._wireManage();
+ this._restoreFocusState(focusState);
return;
}
if (this._view === "about") {
content.innerHTML = this._renderAbout();
+ this._restoreFocusState(focusState);
return;
}
- // detail
content.innerHTML = this._renderDetail();
this._wireDetail();
+ this._restoreFocusState(focusState);
}
_renderStore() {
@@ -488,32 +509,34 @@ class BahmcloudStorePanel extends HTMLElement {
...categories.map((c) => `
`),
].join("");
- const rows = filtered
- .map((r) => {
- const badge = r.source === "custom"
- ? `
Custom`
- : `
Index`;
+ const rows = filtered.map((r) => {
+ const badge = r.source === "custom"
+ ? `
Custom`
+ : `
Index`;
- const desc = r.description || r.meta_description || r.provider_description || "No description available.";
- const creator = r.owner ? `Creator: ${r.owner}` : "Creator: -";
- const cat = r.category ? `Category: ${r.category}` : null;
- const metaSrc = r.meta_source ? `Meta: ${r.meta_source}` : null;
- const lineBits = [creator, cat, metaSrc].filter(Boolean);
+ const desc = r.description || "No description available.";
- return `
-
-
-
-
${this._esc(r.name)}
-
${this._esc(desc)}
-
${this._esc(lineBits.join(" · "))}
-
- ${badge}
+ const creator = r.owner ? `Creator: ${r.owner}` : "Creator: -";
+ const cat = r.category ? `Category: ${r.category}` : null;
+ const metaSrc = r.meta_source ? `Meta: ${r.meta_source}` : null;
+
+ const latest = r.latest_version ? `Latest: ${r.latest_version}` : "Latest: unknown";
+
+ const lineBits = [creator, latest, cat, metaSrc].filter(Boolean);
+
+ return `
+
+
+
+
${this._esc(r.name)}
+
${this._esc(desc)}
+
${this._esc(lineBits.join(" · "))}
+ ${badge}
- `;
- })
- .join("");
+
+ `;
+ }).join("");
return `
@@ -563,10 +586,13 @@ class BahmcloudStorePanel extends HTMLElement {
? `
Custom`
: `
Index`;
- const desc = r.description || r.meta_description || r.provider_description || "No description available.";
+ 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.category ? `Category: ${r.category}` : null,
r.meta_author ? `Author: ${r.meta_author}` : null,
@@ -580,8 +606,14 @@ class BahmcloudStorePanel extends HTMLElement {
? `
README
-
Rendered Markdown (fallback to raw text if needed).
+
+
+ Show raw Markdown
+
+
${this._esc(this._readmeText)}
+
+
`
: `
@@ -628,9 +660,6 @@ class BahmcloudStorePanel extends HTMLElement {
}
_wireDetail() {
- // Render README into container:
- // 1) Try ha-markdown (if available)
- // 2) Fallback: raw markdown in
const root = this.shadowRoot;
const container = root.getElementById("readmeContainer");
if (!container) return;
@@ -639,19 +668,22 @@ class BahmcloudStorePanel extends HTMLElement {
if (!this._readmeText) return;
- // Try HA markdown element (best effort)
- try {
- const el = document.createElement("ha-markdown");
- // some HA builds need hass set before content
- try { el.hass = this._hass; } catch (_) {}
- el.content = this._readmeText;
- container.appendChild(el);
- return;
- } catch (_) {
- // fall through
+ // Only attempt ha-markdown if the custom element exists
+ const hasHaMarkdown = typeof customElements !== "undefined" && !!customElements.get("ha-markdown");
+
+ if (hasHaMarkdown) {
+ try {
+ const el = document.createElement("ha-markdown");
+ try { el.hass = this._hass; } catch (_) {}
+ el.content = this._readmeText;
+ container.appendChild(el);
+ return;
+ } catch (_) {
+ // fall through to raw fallback (details already exists)
+ }
}
- // Fallback: raw text
+ // If ha-markdown is missing, show a minimal rendered area:
const pre = document.createElement("pre");
pre.className = "readme";
pre.textContent = this._readmeText;
@@ -697,23 +729,21 @@ class BahmcloudStorePanel extends HTMLElement {
const repos = Array.isArray(this._data.repos) ? this._data.repos : [];
const custom = repos.filter((r) => r.source === "custom");
- const list = custom
- .map((r) => {
- return `
-
-
-
-
${this._esc(r.name)}
-
${this._esc(r.url)}
-
-
-
-
+ const list = custom.map((r) => {
+ return `
+
+
+
+
${this._esc(r.name)}
+
${this._esc(r.url)}
+
+
+
- `;
- })
- .join("");
+
+ `;
+ }).join("");
return `