diff --git a/custom_components/bahmcloud_store/panel/panel.js b/custom_components/bahmcloud_store/panel/panel.js
index 5533828..092ef3f 100644
--- a/custom_components/bahmcloud_store/panel/panel.js
+++ b/custom_components/bahmcloud_store/panel/panel.js
@@ -15,7 +15,8 @@ class BahmcloudStorePanel extends HTMLElement {
this._search = "";
this._category = "all";
- this._provider = "all"; // all|github|gitea|gitlab|other|custom
+ this._filter = "all"; // all|installed|not_installed|updates|custom
+ this._sort = "az"; // az|updates_first|installed_first
this._detailRepoId = null;
this._detailRepo = null;
@@ -562,6 +563,25 @@ class BahmcloudStorePanel extends HTMLElement {
const root = this.shadowRoot;
if (!root) return;
+ // Preserve focus & cursor position for inputs that trigger a re-render (e.g. search).
+ // Without this, mobile browsers may drop focus after each keystroke.
+ const active = root.activeElement;
+ const restore = {
+ id: active && active.id ? String(active.id) : null,
+ start: null,
+ end: null,
+ };
+ try {
+ if (active && (active.tagName === "INPUT" || active.tagName === "TEXTAREA")) {
+ if (typeof active.selectionStart === "number" && typeof active.selectionEnd === "number") {
+ restore.start = active.selectionStart;
+ restore.end = active.selectionEnd;
+ }
+ }
+ } catch (_) {
+ // ignore
+ }
+
const subtitle = root.getElementById("subtitle");
if (subtitle) subtitle.textContent = this._view === "detail" ? "Details" : this._view[0].toUpperCase() + this._view.slice(1);
@@ -604,6 +624,21 @@ class BahmcloudStorePanel extends HTMLElement {
if (this._view === "detail") {
this._wireDetail(); // now always wires buttons
}
+
+ // Restore focus and cursor for the search field after re-render.
+ if (restore.id && this._view === "store") {
+ const el = root.getElementById(restore.id);
+ if (el && (el.tagName === "INPUT" || el.tagName === "TEXTAREA")) {
+ try {
+ el.focus({ preventScroll: true });
+ if (restore.start !== null && restore.end !== null && typeof el.setSelectionRange === "function") {
+ el.setSelectionRange(restore.start, restore.end);
+ }
+ } catch (_) {
+ // ignore
+ }
+ }
+ }
}
_safeText(v) {
@@ -643,16 +678,40 @@ class BahmcloudStorePanel extends HTMLElement {
const cat = this._safeText(r?.category) || "";
if (this._category !== "all" && this._category !== cat) return false;
- const prov = this._safeText(r?.provider) || "other";
- if (this._provider !== "all") {
- if (this._provider === "custom" && r?.source !== "custom") return false;
- if (this._provider !== "custom" && prov !== this._provider) return false;
- }
+ const latest = this._safeText(r?.latest_version);
+ const installed = this._asBoolStrict(r?.installed);
+ const installedVersion = this._safeText(r?.installed_version);
+ const updateAvailable = installed && !!latest && (!installedVersion || latest !== installedVersion);
+
+ if (this._filter === "installed" && !installed) return false;
+ if (this._filter === "not_installed" && installed) return false;
+ if (this._filter === "updates" && !updateAvailable) return false;
+ if (this._filter === "custom" && r?.source !== "custom") return false;
+
return true;
})
.sort((a, b) => {
const an = (this._safeText(a?.name) || "").toLowerCase();
const bn = (this._safeText(b?.name) || "").toLowerCase();
+
+ const alatest = this._safeText(a?.latest_version);
+ const ainstalled = this._asBoolStrict(a?.installed);
+ const ainstalledVersion = this._safeText(a?.installed_version);
+ const aupdate = ainstalled && !!alatest && (!ainstalledVersion || alatest !== ainstalledVersion);
+
+ const blatest = this._safeText(b?.latest_version);
+ const binstalled = this._asBoolStrict(b?.installed);
+ const binstalledVersion = this._safeText(b?.installed_version);
+ const bupdate = binstalled && !!blatest && (!binstalledVersion || blatest !== binstalledVersion);
+
+ if (this._sort === "updates_first") {
+ if (aupdate !== bupdate) return aupdate ? -1 : 1;
+ return an.localeCompare(bn);
+ }
+ if (this._sort === "installed_first") {
+ if (ainstalled !== binstalled) return ainstalled ? -1 : 1;
+ return an.localeCompare(bn);
+ }
return an.localeCompare(bn);
});
@@ -660,8 +719,6 @@ class BahmcloudStorePanel extends HTMLElement {
new Set(repos.map((r) => this._safeText(r?.category)).filter((c) => !!c))
).sort();
- const providers = ["github", "gitlab", "gitea", "other"];
-
const cards = filtered
.map((r) => {
const id = this._safeId(r?.id);
@@ -705,10 +762,17 @@ class BahmcloudStorePanel extends HTMLElement {
${categories.map((c) => ``).join("")}
-