diff --git a/containers/libreportal/frontend/components/updater/css/updater.css b/containers/libreportal/frontend/components/updater/css/updater.css index 71bfb36..02cb4a4 100644 --- a/containers/libreportal/frontend/components/updater/css/updater.css +++ b/containers/libreportal/frontend/components/updater/css/updater.css @@ -88,6 +88,35 @@ .updater-cve-pkg { color: rgba(var(--text-rgb), 0.6); } .updater-cve-fix { margin-left: auto; color: rgba(var(--page-verify-rgb), 0.9); font-size: 0.76rem; } +/* Count pill next to the "Security" detail heading (e.g. "Security 28"). */ +.updater-cve-count { + display: inline-block; vertical-align: middle; margin-left: 4px; + font-size: 0.68rem; font-weight: 700; line-height: 1; padding: 3px 8px; + border-radius: 999px; + background: rgba(var(--page-verify-rgb, var(--accent-rgb)), 0.16); + color: rgb(var(--page-verify-rgb, var(--accent-rgb))); +} + +/* A long CVE list stays compact: once past a handful of rows it caps its height + and scrolls internally rather than pushing the whole page down. The first row + already carries a top border, so no seam is needed at the box edge. */ +.updater-cve-scroll.is-scrollable { + max-height: 260px; overflow-y: auto; + padding-right: 8px; margin-right: -4px; + scrollbar-width: thin; + scrollbar-color: rgba(var(--text-rgb), 0.28) transparent; + /* Bottom-only fade hints there's more below; the top stays crisp so the + worst-severity rows (sorted first) are never dimmed. */ + -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 18px), transparent 100%); + mask-image: linear-gradient(to bottom, #000 calc(100% - 18px), transparent 100%); +} +.updater-cve-scroll.is-scrollable::-webkit-scrollbar { width: 8px; } +.updater-cve-scroll.is-scrollable::-webkit-scrollbar-track { background: transparent; } +.updater-cve-scroll.is-scrollable::-webkit-scrollbar-thumb { + background: rgba(var(--text-rgb), 0.22); border-radius: 999px; +} +.updater-cve-scroll.is-scrollable::-webkit-scrollbar-thumb:hover { background: rgba(var(--text-rgb), 0.34); } + /* ---- Buttons ---- */ .updater-btn { display: inline-flex; align-items: center; gap: 6px; diff --git a/containers/libreportal/frontend/components/updater/js/updater-page.js b/containers/libreportal/frontend/components/updater/js/updater-page.js index 1bc8de4..e3d6aa0 100644 --- a/containers/libreportal/frontend/components/updater/js/updater-page.js +++ b/containers/libreportal/frontend/components/updater/js/updater-page.js @@ -183,6 +183,27 @@ class UpdaterPage { return null; } + sevRank(s) { + const r = { critical: 0, high: 1, medium: 2, low: 3 }; + return r[(s || '').toLowerCase()] ?? 4; + } + + // One CVE list, shared by the standalone Security tab and the per-app expander. + // Sorted worst-first (so the most severe are visible before any scroll) and + // wrapped in a height-capped scroll box once the list is long, so an app with + // dozens of CVEs (e.g. 28) stays compact instead of pushing the page down. + renderCveList(cves) { + const list = [...(cves || [])].sort((x, y) => this.sevRank(x.severity) - this.sevRank(y.severity)); + const items = list.map(c => ` +
No known CVEs. 🎉
'}No known CVEs. 🎉
'}