Initial commit: NexusOS project + desktop theme baseline
Captures the known-good state after theme consolidation and consistency reconciliation: - NexusOS GTK/xfwm4/icon theme assets (assets/themes, management/Mint-Y-Nexus) - Tightened right-click menus, visible separators, no menu icons - assets/themes/install-theme.sh: idempotent restore of all wiring (symlinks, xfconf xsettings+xfwm4, GTK 3/4 settings.ini) - .gitignore excludes venv/ollama/models/runtime/db Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Build NexusOS xfwm4 theme from Mint-Y-Dark-Aqua base.
|
||||
|
||||
- Copies all xfwm4 PNGs + themerc
|
||||
- Aqua pixels (close-active/pressed glyph) → NexusOS green via HSV hue swap
|
||||
- Dark Mint-Y greys repainted to NexusOS surface_bg_alt / border_strong
|
||||
- themerc title text colors updated to NexusOS palette
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import colorsys
|
||||
import shutil
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
THEMES_ROOT = Path(__file__).resolve().parent.parent
|
||||
sys.path.insert(0, str(THEMES_ROOT))
|
||||
import _palette as P # noqa: E402
|
||||
|
||||
from PIL import Image # noqa: E402
|
||||
|
||||
SRC = Path("/usr/share/themes/Mint-Y-Dark-Aqua/xfwm4")
|
||||
OUT = THEMES_ROOT / "NexusOS" / "xfwm4"
|
||||
|
||||
# Aqua → green HSV swap (identical to gtk2 build)
|
||||
AQUA_RGB = (0x1F, 0x9E, 0xDE)
|
||||
AQUA_HSV = colorsys.rgb_to_hsv(*(c / 255 for c in AQUA_RGB))
|
||||
GREEN_HSV = P.hex_to_hsv(P.BRAND_GREEN)
|
||||
S_SCALE = GREEN_HSV[1] / AQUA_HSV[1]
|
||||
V_SCALE = GREEN_HSV[2] / AQUA_HSV[2]
|
||||
HUE_TOLERANCE = 30 / 360
|
||||
SAT_FLOOR = 0.18
|
||||
|
||||
# Gray repaints — direct RGB substitutions
|
||||
GRAY_REMAP = {
|
||||
(34, 34, 38): P.hex_to_rgb(P.SURFACE_BG_ALT),
|
||||
(46, 46, 46): P.hex_to_rgb(P.BORDER_STRONG),
|
||||
(36, 36, 39): P.hex_to_rgb(P.SURFACE_BG_ALT),
|
||||
(76, 76, 80): P.hex_to_rgb(P.BORDER_STRONG),
|
||||
(85, 85, 89): (130, 130, 134),
|
||||
(106, 106, 109): (150, 150, 154),
|
||||
}
|
||||
|
||||
|
||||
def hue_distance(a: float, b: float) -> float:
|
||||
d = abs(a - b)
|
||||
return min(d, 1 - d)
|
||||
|
||||
|
||||
def recolor_pixel(r: int, g: int, b: int, a: int) -> tuple[int, int, int, int]:
|
||||
if a < 8:
|
||||
return (r, g, b, a)
|
||||
# Direct gray remap
|
||||
if (r, g, b) in GRAY_REMAP:
|
||||
nr, ng, nb = GRAY_REMAP[(r, g, b)]
|
||||
return (nr, ng, nb, a)
|
||||
# Aqua → green
|
||||
h, s, v = colorsys.rgb_to_hsv(r / 255, g / 255, b / 255)
|
||||
if s >= SAT_FLOOR and hue_distance(h, AQUA_HSV[0]) <= HUE_TOLERANCE:
|
||||
new_s = min(1.0, s * S_SCALE)
|
||||
new_v = min(1.0, v * V_SCALE)
|
||||
nr, ng, nb = colorsys.hsv_to_rgb(GREEN_HSV[0], new_s, new_v)
|
||||
return (round(nr * 255), round(ng * 255), round(nb * 255), a)
|
||||
return (r, g, b, a)
|
||||
|
||||
|
||||
def recolor_png(path: Path) -> int:
|
||||
im = Image.open(path).convert("RGBA")
|
||||
px = im.load()
|
||||
w, h = im.size
|
||||
changed = 0
|
||||
for y in range(h):
|
||||
for x in range(w):
|
||||
r, g, b, a = px[x, y]
|
||||
new = recolor_pixel(r, g, b, a)
|
||||
if new != (r, g, b, a):
|
||||
px[x, y] = new
|
||||
changed += 1
|
||||
if changed:
|
||||
im.save(path, optimize=True)
|
||||
return changed
|
||||
|
||||
|
||||
THEMERC_REPLACEMENTS = {
|
||||
"active_text_color=#e3e3e3": f"active_text_color=#{P.TEXT_PRIMARY}",
|
||||
"active_text_shadow_color=#e3e3e3": f"active_text_shadow_color=#{P.TEXT_PRIMARY}",
|
||||
"inactive_text_color=#acacac": f"inactive_text_color=#{P.TEXT_SECONDARY}",
|
||||
"inactive_text_shadow_color=#acacac": f"inactive_text_shadow_color=#{P.TEXT_SECONDARY}",
|
||||
}
|
||||
|
||||
|
||||
def patch_themerc(path: Path) -> None:
|
||||
text = path.read_text()
|
||||
for src, dst in THEMERC_REPLACEMENTS.items():
|
||||
text = text.replace(src, dst)
|
||||
path.write_text(text)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
if OUT.exists():
|
||||
shutil.rmtree(OUT)
|
||||
shutil.copytree(SRC, OUT)
|
||||
print(f"Copied {SRC} → {OUT}")
|
||||
|
||||
recolored = 0
|
||||
for png in sorted(OUT.glob("*.png")):
|
||||
changed = recolor_png(png)
|
||||
if changed:
|
||||
recolored += 1
|
||||
print(f"Recolored {recolored} PNGs")
|
||||
|
||||
patch_themerc(OUT / "themerc")
|
||||
print("Patched themerc")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user