diff --git a/src/components/pages/Extensions.svelte b/src/components/pages/Extensions.svelte index b342fee..2ac3112 100644 --- a/src/components/pages/Extensions.svelte +++ b/src/components/pages/Extensions.svelte @@ -17,6 +17,7 @@ let refreshing = $state(false); let filter: Filter = $state("installed"); let search = $state(""); + let langFilter = $state(null); let working = $state(new Set()); let expanded = $state(new Set()); let panel: Panel = $state(null); @@ -100,9 +101,17 @@ const q = search.toLowerCase(); const matchSearch = e.name.toLowerCase().includes(q) || e.lang.toLowerCase().includes(q); const matchFilter = filter === "installed" ? e.isInstalled : filter === "available" ? !e.isInstalled : filter === "updates" ? e.hasUpdate : true; - return matchSearch && matchFilter; + const matchLang = langFilter === null || e.lang === langFilter; + return matchSearch && matchFilter && matchLang; })); + const availableLangs = $derived( + [...new Set(extensions + .filter((e) => filter === "installed" ? e.isInstalled : filter === "available" ? !e.isInstalled : filter === "updates" ? e.hasUpdate : true) + .map((e) => e.lang) + )].sort() + ); + const groups = $derived.by(() => { const map = new Map(); for (const ext of filtered) { const key = baseName(ext.name); if (!map.has(key)) map.set(key, []); map.get(key)!.push(ext); } @@ -121,6 +130,8 @@ { id: "all", label: "All" }, ]; + function setFilter(f: Filter) { filter = f; langFilter = null; } + function toggleExpand(base: string) { const next = new Set(expanded); next.has(base) ? next.delete(base) : next.add(base); @@ -133,7 +144,7 @@

Extensions

{#each FILTERS as f} - {/each} @@ -155,6 +166,15 @@
+ {#if availableLangs.length > 1} +
+ + {#each availableLangs as lang} + + {/each} +
+ {/if} + {#if panel === "apk"}
@@ -311,6 +331,10 @@ .repo-remove { display: flex; align-items: center; justify-content: center; width: 20px; height: 20px; border-radius: var(--radius-sm); color: var(--text-faint); flex-shrink: 0; transition: color var(--t-base), background var(--t-base); } .repo-remove:hover:not(:disabled) { color: var(--color-error); background: var(--bg-overlay); } + .lang-bar { display: flex; align-items: center; gap: 4px; padding: var(--sp-2) var(--sp-6); flex-shrink: 0; flex-wrap: wrap; border-bottom: 1px solid var(--border-dim); } + .lang-pill { font-family: var(--font-ui); font-size: var(--text-2xs); letter-spacing: var(--tracking-wider); text-transform: uppercase; padding: 3px 9px; border-radius: var(--radius-full); border: 1px solid var(--border-dim); background: none; color: var(--text-faint); cursor: pointer; transition: color var(--t-fast), background var(--t-fast), border-color var(--t-fast); } + .lang-pill:hover { color: var(--text-muted); border-color: var(--border-strong); background: var(--bg-raised); } + .lang-pill.active { background: var(--accent-muted); border-color: var(--accent-dim); color: var(--accent-fg); } .tabs { display: flex; gap: 2px; background: var(--bg-raised); border: 1px solid var(--border-dim); border-radius: var(--radius-md); padding: 2px; } .tab { display: flex; align-items: center; gap: 5px; font-family: var(--font-ui); font-size: var(--text-2xs); letter-spacing: var(--tracking-wide); text-transform: uppercase; padding: 4px 10px; border-radius: var(--radius-sm); color: var(--text-faint); white-space: nowrap; transition: background var(--t-base), color var(--t-base); } .tab:hover { color: var(--text-muted); }