feat(web): acrylic theme, node flags and icons, build info

Acrylic cards and topbar over a blurred background, country flags, ISP and
OS icons, per-node version tags, a build info footer and an admin versions
tab. Overview cards drop the sparkline and widen the hover area.
This commit is contained in:
justme committed 2026-10-06 21:22:04 +08:00
1 parent 1e5c660d05
commit 83f54978d2
29 files changed
+1310 -143

No files matched your search

+69
View File
@@ -0,0 +1,69 @@
import {
siAlmalinux,
siAlpinelinux,
siApple,
siArchlinux,
siCentos,
siDebian,
siFedora,
siFreebsd,
siKalilinux,
siLinux,
siLinuxmint,
siNixos,
siOpensuse,
siOpenwrt,
siRaspberrypi,
siRedhat,
siRockylinux,
siUbuntu,
} from 'simple-icons'
interface Glyph {
/** 24×24 的图形路径。 */
path: string
/** 品牌色(6 位十六进制);黑色的图标不填,用文字颜色。 */
color?: string
}
const fromSimple = (icon: { path: string; hex: string }, useBrandColor = true): Glyph => ({ path: icon.path, color: useBrandColor ? icon.hex : undefined })
/** Windows 的四个窗格(simple-icons 没有收,自己画的最简形状)。 */
const WINDOWS: Glyph = { path: 'M2 4.6 10.4 3.4v7.8H2zM11.6 3.2 22 1.8v9.4H11.6zM2 12.8h8.4v7.8L2 19.4zM11.6 12.8H22v9.4l-10.4-1.4z', color: '0078D4' }
/** 按系统名挑图标:先看 Windows、macOS,再按发行版名字找,认不出的 Linux 用企鹅。 */
function pickGlyph(os: string | null | undefined, name: string | null | undefined): Glyph | undefined {
if (os === 'windows') return WINDOWS
if (os === 'macos') return fromSimple(siApple, false)
const n = (name ?? '').toLowerCase()
if (n.includes('debian')) return fromSimple(siDebian)
if (n.includes('ubuntu')) return fromSimple(siUbuntu)
if (n.includes('alpine')) return fromSimple(siAlpinelinux)
if (n.includes('openwrt')) return fromSimple(siOpenwrt)
if (n.includes('raspbian') || n.includes('raspberry')) return fromSimple(siRaspberrypi)
if (n.includes('kali')) return fromSimple(siKalilinux)
if (n.includes('mint')) return fromSimple(siLinuxmint)
if (n.includes('rocky')) return fromSimple(siRockylinux)
if (n.includes('alma')) return fromSimple(siAlmalinux, false)
if (n.includes('centos')) return fromSimple(siCentos)
if (n.includes('fedora')) return fromSimple(siFedora)
if (n.includes('red hat') || n.includes('rhel')) return fromSimple(siRedhat)
if (n.includes('arch')) return fromSimple(siArchlinux)
if (n.includes('suse')) return fromSimple(siOpensuse)
if (n.includes('nixos')) return fromSimple(siNixos)
if (n.includes('freebsd')) return fromSimple(siFreebsd)
if (os === 'linux') return fromSimple(siLinux, false)
return undefined
}
/** 系统的小图标(Debian、Ubuntu、Windows、macOS……)。认不出的系统不画。 */
export function OsIcon({ os, name }: { os: string | null | undefined; name: string | null | undefined }) {
const glyph = pickGlyph(os, name)
if (!glyph) return null
return (
<svg className="os-icon" viewBox="0 0 24 24" role="img" aria-label={name ?? os ?? ''} style={glyph.color ? ({ '--os-c': `#${glyph.color}` } as React.CSSProperties) : undefined}>
<title>{name ?? os ?? ''}</title>
<path d={glyph.path} className={glyph.color ? 'brand' : 'plain'} />
</svg>
)
}