diff --git a/AppImage/components/lxc-app-panel.tsx b/AppImage/components/lxc-app-panel.tsx index f55a0c6b..43fefdd6 100644 --- a/AppImage/components/lxc-app-panel.tsx +++ b/AppImage/components/lxc-app-panel.tsx @@ -391,30 +391,32 @@ const EMPTY_APP: AppConfig = { logo_url: "", } -const SELFHST_WEBP_BASE = "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp" -const SELFHST_THEME_LOGOS: Record = { - frigate: { - lightTheme: `${SELFHST_WEBP_BASE}/frigate-dark.webp`, - darkTheme: `${SELFHST_WEBP_BASE}/frigate-light.webp`, - }, - "llama-cpp": { - lightTheme: `${SELFHST_WEBP_BASE}/llama-cpp.webp`, - darkTheme: `${SELFHST_WEBP_BASE}/llama-cpp-light.webp`, - }, - portainer: { - lightTheme: `${SELFHST_WEBP_BASE}/portainer-dark.webp`, - darkTheme: `${SELFHST_WEBP_BASE}/portainer-light.webp`, - }, - vaultwarden: { - lightTheme: `${SELFHST_WEBP_BASE}/vaultwarden.webp`, - darkTheme: `${SELFHST_WEBP_BASE}/vaultwarden-light.webp`, - }, -} +// selfh.st publishes a `-light` variant of the logos that are dark, for dark +// backgrounds. These two ship a light logo by default, so the light theme +// takes their `-dark` variant. +const SELFHST_LIGHT_BY_DEFAULT = new Set(["frigate", "portainer"]) function selfhstThemeLogos(src: string) { - if (!src.toLowerCase().includes("cdn.jsdelivr.net/gh/selfhst/icons")) return null - const match = src.toLowerCase().match(/\/(frigate|llama-cpp|portainer|vaultwarden)(?:-(?:dark|light))?\.webp(?:[?#].*)?$/) - return match ? SELFHST_THEME_LOGOS[match[1]] : null + const match = src.match(/^(https:\/\/cdn\.jsdelivr\.net\/gh\/selfhst\/icons@[^/]+\/webp\/)([a-z0-9-]+?)(?:-(?:dark|light))?\.webp(?:[?#].*)?$/i) + if (!match) return null + const [, base, name] = match + return { + lightTheme: `${base}${name}${SELFHST_LIGHT_BY_DEFAULT.has(name.toLowerCase()) ? "-dark" : ""}.webp`, + darkTheme: `${base}${name}-light.webp`, + } +} + +// A logo without the variant keeps the recorded one. +function FallbackLogo({ src, fallback, className }: { src: string; fallback: string; className: string }) { + const [failed, setFailed] = useState(false) + useEffect(() => setFailed(false), [src]) + return ( + { + if (!failed && fallback !== src) setFailed(true) + else e.currentTarget.style.display = "none" + }} /> + ) } const LOCAL_LOGO_CACHE_PATH = /^\/api\/vms\/[1-9]\d*\/apps\/logo-cache\/[a-f0-9]{32}$/ @@ -460,8 +462,8 @@ export function ThemeAwareLogo({ src, className }: { src: string; className: str } return ( <> - - + +