#!/usr/bin/env python3 """ NexusOS action-icon generator. Builds the icons the XFCE logoff dialog (xfce4-session-logout) and the Whisker menu command buttons request — shutdown / reboot / log-out / suspend / hibernate / switch-user / lock-screen / settings — in the NexusOS palette (white glyph on a deep-purple disc with a lime ring). Emits one SVG per icon into actions/, rasterizes to ~/.icons/NexusOS//actions/.png at 16/22/24/32/48/64/128, then writes the alias copies (xfsm-*, system-suspend-hibernate, and the settings-manager app-icon names that Whisker's Settings button uses). Run: python3 build_actions.py # SVGs + PNGs + aliases python3 build_actions.py --svg-only # just rewrite SVGs """ from pathlib import Path import shutil import subprocess import sys ROOT = Path(__file__).resolve().parent ACTIONS = ROOT / "actions" ICONS_OUT = Path.home() / ".icons" / "NexusOS" SIZES = [16, 22, 24, 32, 48, 64, 128] # ── colors (mirror gtk-3.0/colors.css) ──────────────────────────────── BODY = "#5e0066" # brand_purple_dark — disc fill RING = "#8cc63f" # brand_green — disc ring GLYPH = "#f2f2f2" # text_primary — glyph # ── master: purple disc + lime ring, viewBox 0 0 128 128 ────────────── # Glyph area: a ~70px box centered on (64, 64). DISC = f' \n' def _gear_glyph() -> str: """Settings cog: 8 teeth on a white disc with a punched centre hole.""" teeth = "".join( f' \n' for k in range(8) ) return ( " \n" + teeth + f' \n' + f' \n' ) # ── glyph fragments — white, centered on (64, 64) ───────────────────── GLYPHS = { "system-shutdown": f"""\ """, "system-reboot": f"""\ """, "system-log-out": f"""\ """, "system-suspend": f"""\ """, "system-hibernate": f"""\ """, "system-switch-user": f"""\ """, "system-lock-screen": f"""\ """, "preferences-desktop": _gear_glyph(), # ── Whisker menu application-category glyphs ────────────────────── "applications-internet": f"""\ """, "applications-development": f"""\ """, "applications-multimedia": f"""\ """, "applications-office": f"""\ """, "applications-graphics": f"""\ """, "applications-system": f"""\ """, "applications-utilities": f"""\ """, "applications-games": f"""\ """, "applications-science": f"""\ """, } # Extra filenames that render the same art (XFCE's xfsm-* lookup names, # the hybrid-sleep alias, and the Whisker command-button names). ALIASES = { "xfsm-logout": "system-log-out", "xfsm-reboot": "system-reboot", "xfsm-shutdown": "system-shutdown", "xfsm-suspend": "system-suspend", "xfsm-hibernate": "system-hibernate", "xfsm-switch-user": "system-switch-user", "system-suspend-hibernate": "system-suspend", "xfsm-lock": "system-lock-screen", "preferences-system": "preferences-desktop", "org.xfce.settings.manager": "preferences-desktop", "xfce4-settings-manager": "preferences-desktop", # XFCE's "Accessories" menu uses xfce-accessories.directory # (Icon=applications-accessories), NOT the freedesktop Utility name, so # it needs the same wrench/tools badge as applications-utilities. "applications-accessories": "applications-utilities", } def svg_for(glyph: str) -> str: return ( '\n' '\n' f"{DISC}{glyph}\n" ) def write_svgs() -> list[str]: ACTIONS.mkdir(parents=True, exist_ok=True) for name, glyph in GLYPHS.items(): (ACTIONS / f"{name}.svg").write_text(svg_for(glyph)) return list(GLYPHS) def render_pngs(names: list[str]) -> None: for name in names: svg = ACTIONS / f"{name}.svg" for size in SIZES: out_dir = ICONS_OUT / f"{size}x{size}" / "actions" out_dir.mkdir(parents=True, exist_ok=True) out = out_dir / f"{name}.png" subprocess.run( [ "inkscape", str(svg), "--export-type=png", f"--export-filename={out}", f"--export-width={size}", f"--export-height={size}", ], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, ) print(f" rendered {name} @ {size}") def write_aliases() -> None: for alias, src in ALIASES.items(): for size in SIZES: d = ICONS_OUT / f"{size}x{size}" / "actions" src_png = d / f"{src}.png" if src_png.exists(): shutil.copy2(src_png, d / f"{alias}.png") print(f" alias {alias} -> {src}") def main() -> None: svg_only = "--svg-only" in sys.argv names = write_svgs() print(f"Wrote {len(names)} SVGs into {ACTIONS}") if svg_only: return render_pngs(names) write_aliases() subprocess.run( ["gtk-update-icon-cache", "-f", str(ICONS_OUT)], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, ) print("\nDone. Reload: xfdesktop --reload & xfce4-panel -r") if __name__ == "__main__": main()