diff --git a/custom_components/bahmcloud_store/panel/panel.js b/custom_components/bahmcloud_store/panel/panel.js
index 0b4feb8..f8c1b91 100644
--- a/custom_components/bahmcloud_store/panel/panel.js
+++ b/custom_components/bahmcloud_store/panel/panel.js
@@ -21,7 +21,7 @@ class BahmcloudStorePanel extends HTMLElement {
this._readmeLoading = false;
this._readmeText = null;
- this._readmeHtml = null; // backend may provide; we don't rely on it anymore
+ this._readmeHtml = null; // backend may provide; client renderer does not rely on it
this._readmeError = null;
}
@@ -331,10 +331,17 @@ class BahmcloudStorePanel extends HTMLElement {
details{ margin-top: 10px; }
summary{ cursor:pointer; color: var(--bcs-accent); font-weight: 900; }
- /* Pretty Markdown container spacing (ha-markdown renders inside) */
+ /* Pretty Markdown container */
.md { line-height: 1.65; font-size: 14px; }
.md :is(h1,h2,h3){ margin: 18px 0 10px; }
.md :is(p,ul,ol,pre,blockquote,table){ margin: 10px 0; }
+ .md pre { overflow:auto; padding:12px; border-radius:14px; border:1px solid var(--divider-color); }
+ .md code { padding:2px 6px; border-radius:8px; border:1px solid var(--divider-color); }
+ .md blockquote { border-left:4px solid var(--bcs-accent); padding:8px 12px; border-radius:12px;
+ background: color-mix(in srgb, var(--bcs-accent) 8%, var(--card-background-color)); }
+ .md table{ width:100%; border-collapse: collapse; }
+ .md th,.md td{ border:1px solid var(--divider-color); padding:8px; text-align:left; }
+ .md img{ max-width:100%; height:auto; border-radius:12px; }
@@ -659,45 +666,79 @@ class BahmcloudStorePanel extends HTMLElement {
`;
}
+ _mdToHtml(markdown) {
+ const md = String(markdown || "");
+ if (!md.trim()) return "";
+
+ // Prefer HA's frontend libs if present
+ const markedObj = window.marked;
+ const domPurify = window.DOMPurify;
+
+ let html = "";
+
+ try {
+ // marked can be a function or an object with parse()
+ if (markedObj && typeof markedObj.parse === "function") {
+ html = markedObj.parse(md, { breaks: true, gfm: true });
+ } else if (typeof markedObj === "function") {
+ html = markedObj(md);
+ } else {
+ return "";
+ }
+ } catch (_) {
+ return "";
+ }
+
+ try {
+ if (domPurify && typeof domPurify.sanitize === "function") {
+ html = domPurify.sanitize(html);
+ }
+ } catch (_) {}
+
+ return html;
+ }
+
+ _postprocessRenderedMarkdown(container) {
+ if (!container) return;
+
+ // Make links open in new tab
+ try {
+ const links = container.querySelectorAll("a[href]");
+ links.forEach((a) => {
+ a.setAttribute("target", "_blank");
+ a.setAttribute("rel", "noreferrer noopener");
+ });
+ } catch (_) {}
+ }
+
_wireDetail() {
const root = this.shadowRoot;
const mount = root.getElementById("readmePretty");
if (!mount) return;
- // Always prefer HA's frontend markdown renderer to get a professional look.
- // This uses the same renderer concept as HA's Markdown card (Marked.js). :contentReference[oaicite:1]{index=1}
if (this._readmeText) {
- try {
- mount.innerHTML = "";
+ const html = this._mdToHtml(this._readmeText);
- const el = document.createElement("ha-markdown");
- // Ensure it has access to HA context (themes, link handling, etc.)
- el.hass = this._hass;
-
- // Different HA versions use either "content" or "markdown" internally;
- // setting both is harmless and maximizes compatibility.
- el.content = this._readmeText;
- el.markdown = this._readmeText;
-
- // Some versions support these flags; safe to set even if ignored.
- el.allowHtml = false;
- el.breaks = true;
-
- mount.appendChild(el);
+ if (html) {
+ mount.innerHTML = html;
+ this._postprocessRenderedMarkdown(mount);
return;
- } catch (_) {
- // fall through to html/raw fallback
}
- }
- // Fallback: if backend provided sanitized HTML, use it
- if (this._readmeHtml) {
- mount.innerHTML = this._readmeHtml;
+ // Fallback: if backend provided sanitized HTML, use it
+ if (this._readmeHtml) {
+ mount.innerHTML = this._readmeHtml;
+ this._postprocessRenderedMarkdown(mount);
+ return;
+ }
+
+ // Last resort
+ mount.innerHTML = `
Markdown renderer not available on this client. Use “Show raw Markdown”.
`;
return;
}
- // Last resort: show nothing here (raw is still available via details)
- mount.innerHTML = `
Unable to render Markdown on this client.
`;
+ // No readme text loaded
+ mount.innerHTML = "";
}
_renderFabs() {