Files
pulsenode/scripts/extract-brand-icons.py
T
valknar 4308169608
CI / Static checks (push) Successful in 34s
CI / Build and push image (push) Skipped
feat: brand icons for widgets, fix stale accent color
Two separate issues, both real bugs:

- Dark mode was showing light blue instead of green: config.yml still
  had a leftover --pn-accent: #38bdf8 override from before the design
  redesign, which won via the runtime theme injector regardless of
  what app/globals.css defaulted to. Removed the override and pushed
  the dark-mode default from a soft mint (#5ee08c) to an actual neon
  green (#39ff88) - closer to what "neon" means and closer to the real
  phosphor-green a cardiac monitor trace uses, which fits the pulse
  metaphor better than the pastel version did.

- No per-service brand icons anywhere. Added an optional `icon` field
  to docker/bookmark widgets (a key into lib/brand-icons.ts) rather
  than guessing from containerName, since a heuristic would silently
  misfire for anyone's own container naming. database widgets derive
  their icon automatically from `engine` (postgres/redis) since that's
  already required and unambiguous. Icon path data comes from the CC0
  simple-icons package via scripts/extract-brand-icons.py, extracted
  and committed as plain TS data (lib/brand-icons.ts) rather than
  imported live - keeps it self-hosted (no unpkg.com/CDN calls at
  runtime, matching the fonts) and pinned regardless of dependency
  updates. Rendered monochrome (currentColor, not each brand's own
  hex) to stay consistent with the single-accent design rather than a
  dozen competing colors - only the shape carries the identity.

Wired real icons into config.yml for every service that has one:
traefik, coolify, gitea (+ gitea_runner), immich (+ immich_ml), n8n,
passbolt, umami, headscale, plus postgresql/redis on every database
widget. code and mailpit have no brand match in the curated set, so
they render without one rather than a wrong/generic substitute.

Verified in the actual rendered page: compiled CSS confirms
--pn-accent is #39ff88 (dark) / #339a5a (light) with no trace of the
old #38bdf8, and all 18 expected icon instances (10 docker + 8
database widgets) are present.
2026-08-17 17:55:27 +02:00

57 lines
1.7 KiB
Python

#!/usr/bin/env python3
"""Regenerates lib/brand-icons.ts from the `simple-icons` package.
Run from the repo root after `pnpm add simple-icons` has installed it:
python3 scripts/extract-brand-icons.py
Add a new service by adding a "our key" -> "simple-icons slug" entry below.
Slugs are the filenames under node_modules/simple-icons/icons/ (without the
.svg extension).
"""
import json
import re
from pathlib import Path
ICONS = {
"traefik": "traefikproxy",
"coolify": "coolify",
"gitea": "gitea",
"docker": "docker",
"immich": "immich",
"n8n": "n8n",
"passbolt": "passbolt",
"umami": "umami",
"postgresql": "postgresql",
"redis": "redis",
"headscale": "tailscale",
}
ROOT = Path(__file__).resolve().parent.parent
ICONS_DIR = ROOT / "node_modules" / "simple-icons" / "icons"
OUT_PATH = ROOT / "lib" / "brand-icons.ts"
HEADER = """// Path data extracted from the `simple-icons` package (CC0) at build time -
// see scripts/extract-brand-icons.py. Rendered monochrome (currentColor) to
// stay consistent with the single-accent design rather than a dozen
// competing brand colors; the shape alone is enough to be recognizable.
export const BRAND_ICON_PATHS: Record<string, string> = {
"""
def main() -> None:
lines = [HEADER]
for key, slug in ICONS.items():
svg = (ICONS_DIR / f"{slug}.svg").read_text()
d_matches = re.findall(r'<path d="([^"]+)"', svg)
combined = " ".join(d_matches)
lines.append(f" {json.dumps(key)}: {json.dumps(combined)},\n")
lines.append("};\n")
OUT_PATH.write_text("".join(lines))
print(f"Wrote {OUT_PATH} ({len(ICONS)} icons)")
if __name__ == "__main__":
main()