.
This commit is contained in:
@@ -15,6 +15,7 @@ class BahmcloudStorePanel extends HTMLElement {
|
||||
|
||||
this._search = "";
|
||||
this._category = "all";
|
||||
this._provider = "all"; // NEW: provider filter (all|github|gitea|gitlab|other|custom)
|
||||
|
||||
this._detailRepoId = null;
|
||||
this._detailRepo = null;
|
||||
@@ -268,12 +269,20 @@ class BahmcloudStorePanel extends HTMLElement {
|
||||
.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; }
|
||||
.chips{ display:flex; gap:8px; flex-wrap:wrap; margin-bottom:12px; }
|
||||
|
||||
.chip{
|
||||
display:inline-flex; align-items:center; gap:8px;
|
||||
padding:6px 10px; border-radius:999px;
|
||||
border:1px solid var(--divider-color);
|
||||
background:var(--card-background-color);
|
||||
cursor:pointer; user-select:none; font-weight:800; font-size:12px;
|
||||
}
|
||||
.chip strong{ font-size:12px; }
|
||||
.chip.active{
|
||||
border-color:var(--bcs-accent);
|
||||
box-shadow:0 0 0 2px color-mix(in srgb, var(--bcs-accent) 18%, transparent);
|
||||
}
|
||||
|
||||
input, select{
|
||||
@@ -494,6 +503,9 @@ class BahmcloudStorePanel extends HTMLElement {
|
||||
const repos = Array.isArray(this._data.repos) ? this._data.repos : [];
|
||||
const categories = this._computeCategories(repos);
|
||||
|
||||
// Provider stats (for overview)
|
||||
const stats = this._computeProviderStats(repos);
|
||||
|
||||
const filtered = repos
|
||||
.filter((r) => {
|
||||
const q = (this._search || "").trim().toLowerCase();
|
||||
@@ -504,6 +516,12 @@ class BahmcloudStorePanel extends HTMLElement {
|
||||
.filter((r) => {
|
||||
if (this._category === "all") return true;
|
||||
return (r.category || "").toLowerCase() === this._category;
|
||||
})
|
||||
.filter((r) => {
|
||||
if (this._provider === "all") return true;
|
||||
if (this._provider === "custom") return r.source === "custom";
|
||||
const pv = (r.provider || "other").toLowerCase();
|
||||
return pv === this._provider;
|
||||
});
|
||||
|
||||
const options = [
|
||||
@@ -516,6 +534,8 @@ class BahmcloudStorePanel extends HTMLElement {
|
||||
),
|
||||
].join("");
|
||||
|
||||
const providerChips = this._renderProviderChips(stats);
|
||||
|
||||
const rows = filtered
|
||||
.map((r) => {
|
||||
const badge =
|
||||
@@ -547,19 +567,70 @@ class BahmcloudStorePanel extends HTMLElement {
|
||||
.join("");
|
||||
|
||||
return `
|
||||
<div class="card">
|
||||
<div><strong>Providers</strong></div>
|
||||
<div class="chips" id="providerChips">${providerChips}</div>
|
||||
</div>
|
||||
|
||||
<div class="filters">
|
||||
<input id="searchInput" placeholder="Search repositories…" value="${this._esc(this._search)}" />
|
||||
<select id="categorySelect">${options}</select>
|
||||
</div>
|
||||
|
||||
${rows || `<div class="card">No repositories configured.</div>`}
|
||||
${rows || `<div class="card">No repositories match this filter.</div>`}
|
||||
`;
|
||||
}
|
||||
|
||||
_renderProviderChips(stats) {
|
||||
const order = ["all", "github", "gitea", "gitlab", "other", "custom"];
|
||||
const labels = {
|
||||
all: "All",
|
||||
github: "GitHub",
|
||||
gitea: "Gitea",
|
||||
gitlab: "GitLab",
|
||||
other: "Other",
|
||||
custom: "Custom",
|
||||
};
|
||||
const values = {
|
||||
all: stats.total,
|
||||
github: stats.github,
|
||||
gitea: stats.gitea,
|
||||
gitlab: stats.gitlab,
|
||||
other: stats.other,
|
||||
custom: stats.custom,
|
||||
};
|
||||
|
||||
return order
|
||||
.map((key) => {
|
||||
const count = values[key] ?? 0;
|
||||
const active = this._provider === key ? " active" : "";
|
||||
return `<div class="chip${active}" data-prov="${key}"><strong>${labels[key]}</strong> ${count}</div>`;
|
||||
})
|
||||
.join("");
|
||||
}
|
||||
|
||||
_computeProviderStats(repos) {
|
||||
const s = { total: 0, github: 0, gitea: 0, gitlab: 0, other: 0, custom: 0 };
|
||||
if (!Array.isArray(repos)) return s;
|
||||
|
||||
for (const r of repos) {
|
||||
s.total += 1;
|
||||
if (r.source === "custom") s.custom += 1;
|
||||
|
||||
const p = (r.provider || "other").toLowerCase();
|
||||
if (p === "github") s.github += 1;
|
||||
else if (p === "gitea") s.gitea += 1;
|
||||
else if (p === "gitlab") s.gitlab += 1;
|
||||
else s.other += 1;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
_wireStore() {
|
||||
const root = this.shadowRoot;
|
||||
const search = root.getElementById("searchInput");
|
||||
const cat = root.getElementById("categorySelect");
|
||||
const chips = root.getElementById("providerChips");
|
||||
|
||||
if (search) {
|
||||
search.addEventListener("input", (e) => {
|
||||
@@ -575,6 +646,17 @@ class BahmcloudStorePanel extends HTMLElement {
|
||||
});
|
||||
}
|
||||
|
||||
if (chips) {
|
||||
for (const c of chips.querySelectorAll("[data-prov]")) {
|
||||
c.addEventListener("click", () => {
|
||||
const key = c.getAttribute("data-prov");
|
||||
if (!key) return;
|
||||
this._provider = key; // set filter
|
||||
this._update();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
for (const card of root.querySelectorAll("[data-repo]")) {
|
||||
card.addEventListener("click", () => {
|
||||
const id = card.getAttribute("data-repo");
|
||||
@@ -666,18 +748,43 @@ class BahmcloudStorePanel extends HTMLElement {
|
||||
`;
|
||||
}
|
||||
|
||||
_wireDetail() {
|
||||
const root = this.shadowRoot;
|
||||
const mount = root.getElementById("readmePretty");
|
||||
if (!mount) return;
|
||||
|
||||
if (this._readmeText) {
|
||||
const html = this._mdToHtml(this._readmeText);
|
||||
|
||||
if (html) {
|
||||
mount.innerHTML = html;
|
||||
this._postprocessRenderedMarkdown(mount);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._readmeHtml) {
|
||||
mount.innerHTML = this._readmeHtml;
|
||||
this._postprocessRenderedMarkdown(mount);
|
||||
return;
|
||||
}
|
||||
|
||||
mount.innerHTML = `<div class="muted">Markdown renderer not available on this client. Use “Show raw Markdown”.</div>`;
|
||||
return;
|
||||
}
|
||||
|
||||
mount.innerHTML = "";
|
||||
}
|
||||
|
||||
_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") {
|
||||
@@ -700,8 +807,6 @@ class BahmcloudStorePanel extends HTMLElement {
|
||||
|
||||
_postprocessRenderedMarkdown(container) {
|
||||
if (!container) return;
|
||||
|
||||
// Make links open in new tab
|
||||
try {
|
||||
const links = container.querySelectorAll("a[href]");
|
||||
links.forEach((a) => {
|
||||
@@ -711,36 +816,6 @@ class BahmcloudStorePanel extends HTMLElement {
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
_wireDetail() {
|
||||
const root = this.shadowRoot;
|
||||
const mount = root.getElementById("readmePretty");
|
||||
if (!mount) return;
|
||||
|
||||
if (this._readmeText) {
|
||||
const html = this._mdToHtml(this._readmeText);
|
||||
|
||||
if (html) {
|
||||
mount.innerHTML = html;
|
||||
this._postprocessRenderedMarkdown(mount);
|
||||
return;
|
||||
}
|
||||
|
||||
// Fallback: if backend provided sanitized HTML, use it
|
||||
if (this._readmeHtml) {
|
||||
mount.innerHTML = this._readmeHtml;
|
||||
this._postprocessRenderedMarkdown(mount);
|
||||
return;
|
||||
}
|
||||
|
||||
// Last resort
|
||||
mount.innerHTML = `<div class="muted">Markdown renderer not available on this client. Use “Show raw Markdown”.</div>`;
|
||||
return;
|
||||
}
|
||||
|
||||
// No readme text loaded
|
||||
mount.innerHTML = "";
|
||||
}
|
||||
|
||||
_renderFabs() {
|
||||
const r = this._detailRepo;
|
||||
if (!r) return "";
|
||||
@@ -879,4 +954,4 @@ class BahmcloudStorePanel extends HTMLElement {
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("bahmcloud-store-panel", BahmcloudStorePanel);
|
||||
customElements.define("bahmcloud-store-panel", BahmcloudStorePanel);
|
||||
Reference in New Issue
Block a user